From 77ddef7eb44e2c7fc0010c9f5bab2c628ca333f5 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 22 Aug 2025 11:57:02 +0000 Subject: [PATCH 001/101] =?UTF-8?q?=E2=9E=95=20build(deps):=20Add=20langex?= =?UTF-8?q?tract=20for=20text=20entity=20extraction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 1 + uv.lock | 62 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bcde82d2..8d0770f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,6 +91,7 @@ dependencies = [ "pymupdf4llm>=0.0.17", "pypandoc>=1.15", "python-docx>=1.2.0", + "langextract==1.0.8", ] [project.urls] diff --git a/uv.lock b/uv.lock index b73fc4a6..f660b00d 100644 --- a/uv.lock +++ b/uv.lock @@ -238,7 +238,7 @@ wheels = [ [[package]] name = "aymurai" -version = "1.1.11" +version = "1.1.12.dev0+g695a15fa1.d20250822" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -252,6 +252,7 @@ dependencies = [ { name = "gdown" }, { name = "jiwer" }, { name = "joblib" }, + { name = "langextract" }, { name = "more-itertools" }, { name = "numpy" }, { name = "odfpy" }, @@ -306,6 +307,7 @@ requires-dist = [ { name = "gdown", specifier = "==4.6.0" }, { name = "jiwer", specifier = "==3.0.5" }, { name = "joblib", specifier = ">=1.4.2" }, + { name = "langextract", specifier = "==1.0.8" }, { name = "more-itertools", specifier = ">=10.5.0" }, { name = "numpy", specifier = "<2.0.0" }, { name = "odfpy", specifier = ">=1.4.1" }, @@ -1023,6 +1025,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/0e/0636cc1448a7abc444fb1b3a63655e294e0d2d49092dc3de05241be6d43c/google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73", size = 18306 }, ] +[[package]] +name = "google-genai" +version = "1.31.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "google-auth" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/1b/da30fa6e2966942d7028a58eb7aa7d04544dcc3aa66194365b2e0adac570/google_genai-1.31.0.tar.gz", hash = "sha256:8572b47aa684357c3e5e10d290ec772c65414114939e3ad2955203e27cd2fcbc", size = 233482 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/27/1525bc9cbec58660f0842ebcbfe910a1dde908c2672373804879666e0bb8/google_genai-1.31.0-py3-none-any.whl", hash = "sha256:5c6959bcf862714e8ed0922db3aaf41885bacf6318751b3421bf1e459f78892f", size = 231876 }, +] + [[package]] name = "google-pasta" version = "0.2.0" @@ -1651,6 +1672,32 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474 } +[[package]] +name = "langextract" +version = "1.0.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "aiohttp" }, + { name = "async-timeout" }, + { name = "exceptiongroup" }, + { name = "google-genai" }, + { name = "ml-collections" }, + { name = "more-itertools" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/d8/95666e87f02f3c83420ed030f14085c9c6acd986da336b514443dee6d649/langextract-1.0.8.tar.gz", hash = "sha256:dc4c65058969486faf119d2c0cd82a12ccdff1d9785212b96fd67c52cbbe74c7", size = 78418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/70/f5eba44ee7a8c8fe651b14585618a4ca600dd41a6fdc156b4377f4f53bb1/langextract-1.0.8-py3-none-any.whl", hash = "sha256:17b5985154ec1d58f5dd8fe4d6135d0cd4b4d650a075f8cf36eca705e2459263", size = 84987 }, +] + [[package]] name = "libclang" version = "18.1.1" @@ -1823,6 +1870,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b4/b3/743ffc3f59da380da504d84ccd1faf9a857a1445991ff19bf2ec754163c2/mistune-3.1.0-py3-none-any.whl", hash = "sha256:b05198cf6d671b3deba6c87ec6cf0d4eb7b72c524636eddb6dbf13823b52cee1", size = 53694 }, ] +[[package]] +name = "ml-collections" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, +] + [[package]] name = "more-itertools" version = "10.5.0" From 0bbf0d2c63096877458cfecaf68140c097c35468 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 22 Aug 2025 11:58:50 +0000 Subject: [PATCH 002/101] =?UTF-8?q?=F0=9F=9A=A7=20wip:=20Add=20langextract?= =?UTF-8?q?=20entity=20extraction=20experiment=20notebook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../anonymization/05-langextract.ipynb | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 notebooks/experiments/anonymization/05-langextract.ipynb diff --git a/notebooks/experiments/anonymization/05-langextract.ipynb b/notebooks/experiments/anonymization/05-langextract.ipynb new file mode 100644 index 00000000..0f7ab234 --- /dev/null +++ b/notebooks/experiments/anonymization/05-langextract.ipynb @@ -0,0 +1,239 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "7052227b", + "metadata": {}, + "outputs": [], + "source": [ + "import langextract as lx\n", + "from rich.pretty import pprint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01ef2998", + "metadata": {}, + "outputs": [], + "source": [ + "# 1. Define the prompt and extraction rules\n", + "prompt = \"\"\"\n", + " Sos un asistente especializado en el análisis de documentos judiciales.\n", + " Tu tarea es identificar y extraer menciones de información sensible para su posterior anonimización.\n", + " Debés detectar fragmentos textuales que correspondan a cualquiera de las siguientes entidades:\n", + "\n", + " - \"BANCO\": Nombre de una entidad bancaria, pública o privada.\n", + " - \"CBU\": Código Bancario Uniforme (22 dígitos) de una cuenta.\n", + " - \"CORREO_ELECTRONICO\": Dirección de correo electrónico.\n", + " - \"CUIJ\": Código Único de Identificación Jurídica de causas judiciales.\n", + " - \"CUIT_CUIL\": Número de CUIT o CUIL de una persona física o jurídica.\n", + " - \"DIRECCION\": Dirección postal específica (calle, número, etc.).\n", + " - \"DNI\": Número de Documento Nacional de Identidad u otro documento identificatorio.\n", + " - \"EDAD\": Edad explícita de una persona.\n", + " - \"ESTUDIOS\": Nivel o institución educativa que permita identificar a la persona (ej. \"primario incompleto\", \"secundario completo\", \"Licenciado en…\").\n", + " - \"FECHA\": Fecha completa o parcial (día, mes y/o año).\n", + " - \"LINK\": Enlace o URL a una página web.\n", + " - \"LOC\": Localización geográfica específica (ciudad, barrio, comisaría, etc.).\n", + " - \"MARCA_AUTOMOVIL\": Marca de un vehículo (ej. Toyota, Ford).\n", + " - \"NACIONALIDAD\": Nacionalidad de una persona (ej. \"argentino\", \"brasileña\").\n", + " - \"NUM_ACTUACION\": Número identificatorio de una actuación administrativa o contravencional.\n", + " - \"NUM_CAJA_AHORRO\": Número completo de una caja de ahorro o cuenta bancaria.\n", + " - \"NUM_EXPEDIENTE\": Número de expediente judicial o administrativo.\n", + " - \"NUM_MATRICULA\": Número de matrícula profesional o académica.\n", + " - \"PATENTE_DOMINIO\": Patente o dominio de un vehículo.\n", + " - \"PER\": Nombre y apellido(s) de una persona física. Los nombres inicializados y los apodos también cuentan como información sensible a anonimizar.\n", + " - \"TELEFONO\": Número telefónico (fijo o celular).\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96f85b5f", + "metadata": {}, + "outputs": [], + "source": [ + "# 2. Provide a high-quality example to guide the model\n", + "examples = [\n", + " lx.data.ExampleData(\n", + " text=\"El 5 de mayo de 2023 el señor Fiscal indicó que realizó distintas medidas de prueba y que del resultado surge que tanto la investigada como el menor Juan Pérez se domicilian en la calle Sarmiento 1234, de la localidad de Moreno, por lo que solicitó que se declare la incompetencia en razón del territorio y se envíe el caso al Juzgado de Garantías que corresponda del Departamento Judicial de Moreno, con jurisdicción en el partido de Moreno.\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"FECHA\", extraction_text=\"5 de mayo de 2023\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Juan Pérez\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"DIRECCION\", extraction_text=\"Sarmiento 1234\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"LOC\", extraction_text=\"Moreno\"\n", + " ),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"JUZGADO DE 1RA INSTANCIA EN LO PENAL CONTRAVENCIONAL Y DE FALTAS N°10 SECRETARIA N°19\\nCarlos Gómez sobre 84 - HOMICIDIO CULPOSO Y OTROS\\nNúmero: 52345/2022\\nCUIJ: 12-34567890-1\\nActuación Nro: 2022-009876\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Carlos Gómez\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"NUM_EXPEDIENTE\", extraction_text=\"52345/2022\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"CUIJ\", extraction_text=\"12-34567890-1\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"NUM_ACTUACION\", extraction_text=\"2022-009876\"\n", + " ),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"Acusado: Miguel Torres, DNI 30123456, nacido el 14/02/1990, de 34 años de edad, de nacionalidad paraguaya, género varón cis, con estudios secundarios completos, hizo hasta 3er año porque fue padre joven, con último domicilio en Av. Corrientes 3456, de esta ciudad, donde vive con su hermana y su cuñado Jorge Pérez. Tiene dos hijos a su cargo, de 5 y 8 años. Su hijo de 8 vive con él, su hija de 5 vive con su madre, Laura Fernández.\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Miguel Torres\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"DNI\", extraction_text=\"30123456\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"FECHA\", extraction_text=\"14/02/1990\"\n", + " ),\n", + " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"34\"),\n", + " lx.data.Extraction(\n", + " extraction_class=\"NACIONALIDAD\", extraction_text=\"paraguaya\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"ESTUDIOS\",\n", + " extraction_text=\"estudios secundarios completos\",\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"DIRECCION\",\n", + " extraction_text=\"Av. Corrientes 3456\",\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Jorge Pérez\"\n", + " ),\n", + " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"5\"),\n", + " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"8\"),\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Laura Fernández\"\n", + " ),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"El testigo Juan López dejó asentado su número de contacto: 11-2345-6789. Indicó que la médica Dra. Ana García, MN 12345, asistió al lugar donde se hallaba un vehículo Volkswagen, patente AB123CD.\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Juan López\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"TELEFONO\", extraction_text=\"11-2345-6789\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Ana García\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"NUM_MATRICULA\", extraction_text=\"12345\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"MARCA_AUTOMOVIL\",\n", + " extraction_text=\"Volkswagen\",\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"PATENTE_DOMINIO\", extraction_text=\"AB123CD\"\n", + " ),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"Se identificó una transferencia bancaria con los siguientes datos: CUIT 20-12345678-3, CBU 2850590940090412345671, Caja de Ahorro N° 12345678, Banco Nación.\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"CUIT_CUIL\", extraction_text=\"20-12345678-3\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"CBU\",\n", + " extraction_text=\"2850590940090412345671\",\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"NUM_CAJA_AHORRO\", extraction_text=\"12345678\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"BANCO\", extraction_text=\"Banco Nación\"\n", + " ),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"Para mayor información, comunicarse a fiscalia.central@justicia.gob.ar o visitar el sitio https://justicia.gob.ar/actuaciones.\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"CORREO_ELECTRONICO\",\n", + " extraction_text=\"fiscalia.central@justicia.gob.ar\",\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"LINK\",\n", + " extraction_text=\"https://justicia.gob.ar/actuaciones\",\n", + " ),\n", + " ],\n", + " ),\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a34c40a8", + "metadata": {}, + "outputs": [], + "source": [ + "text = \"La Fiscalía determinó que el objeto de este caso es investigar el hecho que tuvo lugar el día 12 de marzo de 2023 a las 8:50 horas aproximadamente, ocasión en que Carlos Gómez y María Rodriguez estafaron a Juan Pérez por un monto total de pesos treinta y tres mil novecientos sesenta ($33.960).\"\n", + "\n", + "# Run the extraction\n", + "result = lx.extract(\n", + " text_or_documents=text,\n", + " prompt_description=prompt,\n", + " examples=examples,\n", + " model_id=\"llama3.2:3b\",\n", + " model_url=\"http://localhost:11434\",\n", + " fence_output=False,\n", + " use_schema_constraints=False,\n", + ")\n", + "\n", + "pprint(result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb47da66", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.18)", + "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.10.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From c2bc1f2fbfc8406a9ee6206b604e8386d0143189 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 10 Nov 2025 17:00:19 +0000 Subject: [PATCH 003/101] =?UTF-8?q?=E2=9C=A8=20feat:=20Enhance=20entity=20?= =?UTF-8?q?models=20with=20relation=20handling=20and=20canonical=20represe?= =?UTF-8?q?ntation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/meta/entities.py | 94 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/aymurai/meta/entities.py b/aymurai/meta/entities.py index d16c0b68..64170307 100644 --- a/aymurai/meta/entities.py +++ b/aymurai/meta/entities.py @@ -1,4 +1,10 @@ -from pydantic import BaseModel, Field +from __future__ import annotations + +import json +from typing import Any +from uuid import NAMESPACE_URL, UUID, uuid5 + +from pydantic import BaseModel, Field, model_validator class EntityAttributes(BaseModel): @@ -26,6 +32,9 @@ class EntityAttributes(BaseModel): description="Method used on the prediction label", ) aymurai_score: float | None = Field(None, description="Score for prediction") + canonical_entity_id: UUID | None = Field( + None, description="Reference to the canonical entity ID" + ) class Entity(BaseModel): @@ -38,3 +47,86 @@ class Entity(BaseModel): context_pre: str = "" context_post: str = "" attrs: EntityAttributes | None = None + + +class EntityRelation(BaseModel): + """Semantic relation between canonical entities.""" + + relation_id: UUID | None = Field( + None, description="Unique identifier of the relation" + ) + relation_type: str = Field( + description="Type of relation (e.g. identifies, resides_in)" + ) + subject_entity_id: UUID = Field( + description="Identifier of the subject entity participating in the relation" + ) + object_entity_id: UUID = Field( + description="Identifier of the object entity participating in the relation" + ) + attributes: dict[str, Any] = Field( + default_factory=dict, + description="Additional attributes captured for the relation", + ) + + @model_validator(mode="after") + def assign_relation_id(self) -> EntityRelation: + """Populate relation_id deterministically when not provided.""" + + if self.relation_id is not None: + return self + + payload = { + "relation_type": self.relation_type, + "subject_entity_id": str(self.subject_entity_id), + "object_entity_id": str(self.object_entity_id), + "attributes": self.attributes, + } + seed = json.dumps(payload, sort_keys=True, separators=(",", ":")) + object.__setattr__(self, "relation_id", uuid5(NAMESPACE_URL, seed)) + + return self + + +class CanonicalEntity(BaseModel): + """Canonical representation of an entity cluster.""" + + entity_id: UUID | None = Field( + None, description="Unique identifier for the canonical entity" + ) + aymurai_label: str = Field(title="AymurAI label") + canonical_text: str = Field(description="Preferred textual form for the entity") + aliases: list[str] = Field( + default_factory=list, + description="Alternative surface forms observed for the entity", + ) + attributes: dict[str, Any] = Field( + default_factory=dict, description="Additional metadata for the entity" + ) + relations: list[EntityRelation] = Field( + default_factory=list, + description="References to relations involving this entity", + ) + + @model_validator(mode="after") + def validate_entity_id(self) -> CanonicalEntity: + """ + Ensure the canonical entity has an entity_id; if missing, create a surrogate UUID and set it. + + Returns: + CanonicalEntity: the validated model instance (self). + """ + # If entity_id is already set, nothing to do. + if self.entity_id is not None: + return self + + payload = { + "aymurai_label": self.aymurai_label, + "canonical_text": self.canonical_text, + "aliases": sorted(self.aliases), + "attributes": self.attributes, + } + seed = json.dumps(payload, sort_keys=True, separators=(",", ":")) + object.__setattr__(self, "entity_id", uuid5(NAMESPACE_URL, seed)) + + return self From d19bb79879d08efaee2581be766fe517ca0b176a Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 10 Nov 2025 21:22:38 +0000 Subject: [PATCH 004/101] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20JSON=20serial?= =?UTF-8?q?ization=20support=20and=20enhance=20utility=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/utils/json_data.py | 77 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/aymurai/utils/json_data.py b/aymurai/utils/json_data.py index a28db543..ad43164e 100644 --- a/aymurai/utils/json_data.py +++ b/aymurai/utils/json_data.py @@ -1,20 +1,80 @@ import json +from datetime import date, datetime from itertools import groupby -from typing import Union, Iterable, Iterator +from typing import Any, Iterable, Iterator -def save_json(json_data: Union[dict, list[dict]], file_path: str): +def json_serial(obj: Any) -> str: + """ + JSON serializer for objects not serializable by default JSON encoder. + + Args: + obj: The object to serialize. This function currently supports + datetime.date and datetime.datetime objects. + + Returns: + str: The ISO 8601 formatted string representation of the date or datetime object. + + Raises: + TypeError: If the object is not of type datetime.date or datetime.datetime. + """ # noqa: E501 + if isinstance(obj, (datetime, date)): + return obj.isoformat() + raise TypeError("Type %s not serializable" % type(obj)) + + +def get_pretty(obj: dict | list[Any]) -> str: + """ + Returns a pretty json string. + + Args: + obj (Union[dict, list[Any]]): the object to be converted to json. + + Returns: + str: the pretty json string. + """ + pretty_json_str = json.dumps(obj, indent=4, ensure_ascii=False, default=json_serial) + return pretty_json_str + + +def save_json(json_data: dict | list[dict], file_path: str) -> None: + """ + + Saves a JSON object to a file. + + Args: + json_data (dict | list[dict]): The JSON data to be saved. + file_path (str): The path to the file where the JSON data will be saved. + """ with open(file_path, "w") as f: f.write(json.dumps(json_data, indent=4, ensure_ascii=False)) -def load_json(json_file_path: str) -> Union[dict, list[dict]]: +def load_json(json_file_path: str) -> dict | list[dict]: + """ + Loads a JSON object from a file. + + Args: + json_file_path (str): The path to the JSON file. + + Returns: + dict | list[dict]: The loaded JSON object. + """ with open(json_file_path, "r") as f: content = json.loads(f.read()) return content def get_unique(json_list: list[dict]) -> Iterator[dict]: + """ + Get unique json objects from a list of json objects. + + Args: + json_list (list[dict]): The list of JSON objects. + + Returns: + Iterator[dict]: An iterator of unique JSON objects. + """ unique = set(map(json.dumps, json_list)) unique = map(json.loads, unique) return unique @@ -23,7 +83,18 @@ def get_unique(json_list: list[dict]) -> Iterator[dict]: def group_by_key( json_iter: Iterable[dict], group_key: str, sort_key: str = "" ) -> Iterator[list[dict]]: + """ + Groups a list of JSON objects by a specified key. + + Args: + json_iter (Iterable[dict]): An iterable of JSON objects to be grouped. + group_key (str): The key to group the JSON objects by. + sort_key (str, optional): An optional key to sort the JSON objects before grouping. + Defaults to "". + Returns: + Iterator[list[dict]]: An iterator of lists of JSON objects grouped by the specified key. + """ if sort_key: json_iter = sorted(json_iter, key=lambda x: x[sort_key]) From 1c0edb5dbfb9ecf0e79893f9d6ba0f1014e708aa Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 10 Nov 2025 21:30:01 +0000 Subject: [PATCH 005/101] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20ML=20dep?= =?UTF-8?q?endencies=20and=20refresh=20uv.lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 9 +- uv.lock | 464 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 445 insertions(+), 28 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 20a4419b..5e2711b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,10 +76,10 @@ dependencies = [ "cachetools>=5.5.0", "diskcache>=5.6.3", "scipy<1.14.1", - "torch==1.12.1", # for decision model - "torchtext==0.13.1", # for decision model + "torch==2.9.0", + "torchtext==0.18.0", "pytorch-lightning==1.8.3.post1", - "tensorflow_text==2.10.0", # last version compatible with Windows + "tensorflow_text==2.10.0", # last version compatible with Windows "psutil==6.1.0", "sqlmodel==0.0.22", "alembic>=1.13.3", @@ -91,6 +91,7 @@ dependencies = [ "pypandoc>=1.15", "python-docx>=1.2.0", "langextract==1.0.8", + "ollama==0.6.0", ] [project.urls] @@ -114,6 +115,7 @@ dev = [ "nbstripout>=0.8.0", "jupyter>=1.1.1", "pip>=24.3.1", + "streamlit>=1.21.0", ] [tool.setuptools.packages.find] @@ -128,6 +130,7 @@ aymurai-api = "aymurai.api.main:main" [tool.setuptools_scm] version_file = "aymurai/version.py" +local_scheme = "no-local-version" [tool.pylint.messages_control] diff --git a/uv.lock b/uv.lock index adad5a1f..d7646eee 100644 --- a/uv.lock +++ b/uv.lock @@ -96,6 +96,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/7e/ac0991d1745f7d755fc1cd381b3990a45b404b4d008fc75e2a983516fbfe/alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5", size = 233565 }, ] +[[package]] +name = "altair" +version = "4.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "entrypoints" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/bf/781b607da4c1a2a7211cd570bd7e22e0accd4deaf1074c32ac7344a09339/altair-4.2.2.tar.gz", hash = "sha256:39399a267c49b30d102c10411e67ab26374156a84b1aeb9fcd15140429ba49c5", size = 740430 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/62/47452306e84d4d2e67f9c559380aeb230f5e6ca84fafb428dd36b96a99ba/altair-4.2.2-py3-none-any.whl", hash = "sha256:8b45ebeaf8557f2d760c5c77b79f02ae12aee7c46c27c06014febab6f849bc87", size = 813630 }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -238,7 +255,7 @@ wheels = [ [[package]] name = "aymurai" -version = "1.1.12" +version = "1.1.13.dev5" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -256,6 +273,7 @@ dependencies = [ { name = "more-itertools" }, { name = "numpy" }, { name = "odfpy" }, + { name = "ollama" }, { name = "psutil" }, { name = "pydantic" }, { name = "pydantic-settings" }, @@ -291,6 +309,7 @@ dev = [ { name = "pre-commit" }, { name = "rich" }, { name = "seaborn" }, + { name = "streamlit" }, ] [package.metadata] @@ -310,6 +329,7 @@ requires-dist = [ { name = "more-itertools", specifier = ">=10.5.0" }, { name = "numpy", specifier = "<2.0.0" }, { name = "odfpy", specifier = ">=1.4.1" }, + { name = "ollama", specifier = "==0.6.0" }, { name = "psutil", specifier = "==6.1.0" }, { name = "pydantic", specifier = ">=2.10.4" }, { name = "pydantic-settings", specifier = ">=2.7.0" }, @@ -328,8 +348,8 @@ requires-dist = [ { name = "tensorflow-hub", specifier = ">=0.16.1" }, { name = "tensorflow-text", specifier = "==2.10.0" }, { name = "textract", specifier = "==1.6.5" }, - { name = "torch", specifier = "==1.12.1" }, - { name = "torchtext", specifier = "==0.13.1" }, + { name = "torch", specifier = "==2.9.0" }, + { name = "torchtext", specifier = "==0.18.0" }, { name = "unidecode", specifier = "==1.3.8" }, { name = "uvicorn", specifier = ">=0.34.0" }, { name = "xmltodict", specifier = "==0.14.2" }, @@ -345,6 +365,7 @@ dev = [ { name = "pre-commit", specifier = ">=4.0.1" }, { name = "rich", specifier = ">=13.9.4" }, { name = "seaborn", specifier = ">=0.13.2" }, + { name = "streamlit", specifier = ">=1.21.0" }, ] [[package]] @@ -401,6 +422,15 @@ css = [ { name = "tinycss2" }, ] +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, +] + [[package]] name = "boto3" version = "1.36.6" @@ -733,6 +763,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 }, ] +[[package]] +name = "entrypoints" +version = "0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/8d/a7121ffe5f402dc015277d2d31eb82d2187334503a011c18f2e78ecbb9b2/entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", size = 13974 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294 }, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -996,6 +1035,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/54/0bbe240c6f59ac9209c2356dc65abd209963851569494376a077e55ef98d/gdown-4.6.0-py3-none-any.whl", hash = "sha256:e75c5aa8be8ea1cac642d4793f884339d887ab5e07aaa57fafa16c8a56a0cde5", size = 14400 }, ] +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.45" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168 }, +] + [[package]] name = "google-auth" version = "2.38.0" @@ -1204,6 +1267,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/39/e1c2c2c6e2356ab6ea81fcfc0a74b044b311d6a91a45300811d9a6077ef7/IMAPClient-2.1.0-py2.py3-none-any.whl", hash = "sha256:3eeb97b9aa8faab0caa5024d74bfde59408fbd542781246f6960873c7bf0dd01", size = 73972 }, ] +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656 }, +] + [[package]] name = "intervaltree" version = "3.1.0" @@ -1903,6 +1978,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/6a/e3691bcc47485f38b09853207c928130571821d187cf174eed5418d45e82/mpld3-0.5.10-py3-none-any.whl", hash = "sha256:80877acce87ea447380fad7374668737505c8c0684aab05238e7c5dc1fab38c1", size = 202561 }, ] +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + [[package]] name = "multidict" version = "6.1.0" @@ -2022,6 +2106,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, ] +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + [[package]] name = "nodeenv" version = "1.9.1" @@ -2075,6 +2168,155 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, + { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, + { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, + { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.3.20" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/9d/3dd98852568fb845ec1f7902c90a22b240fe1cbabda411ccedf2fd737b7b/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b0b960da3842212758e4fa4696b94f129090b30e5122fea3c5345916545cff0", size = 124484616 }, + { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, +] + [[package]] name = "oauthlib" version = "3.2.2" @@ -2102,6 +2344,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/d3/b64c356a907242d719fc668b71befd73324e47ab46c8ebbbede252c154b2/olefile-0.47-py2.py3-none-any.whl", hash = "sha256:543c7da2a7adadf21214938bb79c83ea12b473a4b6ee4ad4bf854e7715e13d1f", size = 114565 }, ] +[[package]] +name = "ollama" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/47/f9ee32467fe92744474a8c72e138113f3b529fc266eea76abfdec9a33f3b/ollama-0.6.0.tar.gz", hash = "sha256:da2b2d846b5944cfbcee1ca1e6ee0585f6c9d45a2fe9467cbcd096a37383da2f", size = 50811 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/c1/edc9f41b425ca40b26b7c104c5f6841a4537bb2552bfa6ca66e81405bb95/ollama-0.6.0-py3-none-any.whl", hash = "sha256:534511b3ccea2dff419ae06c3b58d7f217c55be7897c8ce5868dfb6b219cf7a0", size = 14130 }, +] + [[package]] name = "opt-einsum" version = "3.4.0" @@ -2131,23 +2386,21 @@ wheels = [ [[package]] name = "pandas" -version = "2.2.3" +version = "1.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, { name = "pytz" }, - { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +sdist = { url = "https://files.pythonhosted.org/packages/74/ee/146cab1ff6d575b54ace8a6a5994048380dc94879b0125b25e62edcb9e52/pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1", size = 5203060 } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 }, - { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 }, - { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 }, - { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 }, - { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 }, - { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 }, - { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 }, + { url = "https://files.pythonhosted.org/packages/a9/cd/34f6b0780301be81be804d7aa71d571457369e6131e2b330af2b0fed1aad/pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406", size = 18619230 }, + { url = "https://files.pythonhosted.org/packages/5f/34/b7858bb7d6d6bf4d9df1dde777a11fcf3ff370e1d1b3956e3d0fcca8322c/pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572", size = 11982991 }, + { url = "https://files.pythonhosted.org/packages/b8/6c/005bd604994f7cbede4d7bf030614ef49a2213f76bc3d738ecf5b0dcc810/pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996", size = 10927131 }, + { url = "https://files.pythonhosted.org/packages/27/c7/35b81ce5f680f2dac55eac14d103245cd8cf656ae4a2ff3be2e69fd1d330/pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354", size = 11368188 }, + { url = "https://files.pythonhosted.org/packages/49/e2/79e46612dc25ebc7603dc11c560baa7266c90f9e48537ecf1a02a0dd6bff/pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23", size = 12062104 }, + { url = "https://files.pythonhosted.org/packages/d9/cd/f27c2992cbe05a3e39937f73a4be635a9ec149ec3ca4467d8cf039718994/pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328", size = 10362473 }, ] [[package]] @@ -2495,6 +2748,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 }, ] +[[package]] +name = "pydeck" +version = "0.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, +] + [[package]] name = "pygments" version = "2.19.1" @@ -2504,6 +2770,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, ] +[[package]] +name = "pympler" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/37/c384631908029676d8e7213dd956bb686af303a80db7afbc9be36bc49495/pympler-1.1.tar.gz", hash = "sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424", size = 179954 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766 }, +] + [[package]] name = "pymupdf" version = "1.25.2" @@ -3084,6 +3362,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl", hash = "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", size = 10586 }, ] +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -3185,6 +3472,52 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, ] +[[package]] +name = "streamlit" +version = "1.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { name = "importlib-metadata" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "pympler" }, + { name = "python-dateutil" }, + { name = "requests" }, + { name = "rich" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "tzlocal" }, + { name = "validators" }, + { name = "watchdog", marker = "platform_system != 'Darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/95/89b0f5f51cd006ffb6cc216393793d05029c47e97f140a9bff02f8c291a8/streamlit-1.21.0.tar.gz", hash = "sha256:c72d9639508679c5e411d1f886d213777759501d01975285c049dc30db463c1a", size = 9345809 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/fa/5b010610aa136c44a20de6ed9ec0b36867daf7322d0e3c8e626a99fab11a/streamlit-1.21.0-py2.py3-none-any.whl", hash = "sha256:05862598952a9126e16652caa5bc88eeeea7b2773af252e2daccf1c84ceaaef4", size = 9665093 }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, +] + [[package]] name = "tabulate" version = "0.9.0" @@ -3471,6 +3804,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/69/d21eb253fa91622da25585d362a874fa4710be600f0ea9446d8d0217cec1/tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c", size = 2389192 }, ] +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + [[package]] name = "tomli" version = "2.2.1" @@ -3480,19 +3822,48 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, ] +[[package]] +name = "toolz" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, +] + [[package]] name = "torch" -version = "1.12.1" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/74/7342c7f21449557a8263c925071a55081edd7e9b641404cfe31d6fb71d3b/torch-1.12.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:9c038662db894a23e49e385df13d47b2a777ffd56d9bcd5b832593fab0a7e286", size = 776338835 }, - { url = "https://files.pythonhosted.org/packages/36/b0/4857929aa28dfe26f7de909ebf002b60499edcd7566441a7433df865f9ba/torch-1.12.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4e1b9c14cf13fd2ab8d769529050629a0e68a6fc5cb8e84b4a3cc1dd8c4fe541", size = 55712361 }, - { url = "https://files.pythonhosted.org/packages/b9/25/fc2111599a038aa6c1c618a7dc9246aabc95f899008949ade31213255a0c/torch-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:e9c8f4a311ac29fc7e8e955cfb7733deb5dbe1bdaabf5d4af2765695824b7e0d", size = 162235663 }, - { url = "https://files.pythonhosted.org/packages/37/72/ef80d39a371a9b4a8aadfb22b141972cc67a7075dae69a9d5b8116505ec0/torch-1.12.1-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:976c3f997cea38ee91a0dd3c3a42322785414748d1761ef926b789dfa97c6134", size = 133811424 }, - { url = "https://files.pythonhosted.org/packages/2f/17/8b557dde1cdb5fbe82f90a3f192046c8e508f106456e12f17c87543e6a42/torch-1.12.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:68104e4715a55c4bb29a85c6a8d57d820e0757da363be1ba680fa8cc5be17b52", size = 49099890 }, + { url = "https://files.pythonhosted.org/packages/bb/86/245c240d2138c17ed572c943c289056c2721abab70810d772c6bf5495b28/torch-2.9.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:030bbfe367379ae6a4ae4042b6c44da25383343b8b3c68abaa9c7231efbaf2dd", size = 104213554 }, + { url = "https://files.pythonhosted.org/packages/58/1d/fd1e88ae0948825efcab7dd66d12bec23f05d4d38ed81573c8d453c14c06/torch-2.9.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:51cb63902182a78e90886e8068befd8ea102af4b00e420263591a3d70c7d3c6c", size = 899795167 }, + { url = "https://files.pythonhosted.org/packages/63/5a/496197b45c14982bef4e079b24c61dc108e3ab0d0cc9718dba9f54f45a46/torch-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:3f6aad4d2f0ee2248bac25339d74858ff846c3969b27d14ac235821f055af83d", size = 109310314 }, + { url = "https://files.pythonhosted.org/packages/58/b0/2b4e647b0fc706e88eb6c253d05511865578f5f67b55fad639bf3272a4a1/torch-2.9.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:413e1654c9203733138858780e184d9fc59442f0b3b209e16f39354eb893db9b", size = 74452019 }, ] [[package]] @@ -3511,7 +3882,7 @@ wheels = [ [[package]] name = "torchtext" -version = "0.13.1" +version = "0.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -3520,11 +3891,9 @@ dependencies = [ { name = "tqdm" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/10/6f/673e3533a296f135fecfccdf2519ecfba7e16971dca5c3bc7a8cc9cfd064/torchtext-0.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aa59df8f9542674311fc23aca147a34256c936c25840c4b6ee3fee47d511ad17", size = 1775853 }, - { url = "https://files.pythonhosted.org/packages/2f/bd/5f27d604d3dc6421b3c0bd24c28f73eecb091fce9dbdcbe7af314f252cee/torchtext-0.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:492a22727181edf5a33fa18587f3430ffbb366c2ea835e0f4f8a08be8d9e859c", size = 1966034 }, - { url = "https://files.pythonhosted.org/packages/48/4e/56352383c30b75becd5faaff8d404eb86f3a2282d72ae52aaad705bf22bf/torchtext-0.13.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f56359165eb00ea2ff998b67727f87e7fa32665c1a46610c9b5a2d5d581095e5", size = 1910382 }, - { url = "https://files.pythonhosted.org/packages/3f/fd/e3eef8b5d691cdeb42506fc9fec2a64ab1549039cdf9e7545a171e4a694e/torchtext-0.13.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:b5bf4f9da5326b6e74318b4a5035abfaf166b3abd822565be685f44947adb8d3", size = 1832850 }, - { url = "https://files.pythonhosted.org/packages/b8/9d/7f9b786637d664579b66ae7c6742d662a91b8e6a9f55a34e22c20883d801/torchtext-0.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5f8c1b426bda2f22e5bfd118c779c12f8612ed1077e5e3a491242bb4ab7ac4d3", size = 2239348 }, + { url = "https://files.pythonhosted.org/packages/cc/94/1e805ef3ec6541de75e8a86c32e00be471d98cdcef5035ad26457bc388cf/torchtext-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5826d5bbfe84a3c533e7e97659f72dbff73e1614c00c06709607d17c8446e09c", size = 2137942 }, + { url = "https://files.pythonhosted.org/packages/d7/4f/9953b4d4b79917e03c393484ea8ce8f46a4cc1745f272cc371550fb7fc05/torchtext-0.18.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:3dc446f74aaa9aebab045fbefd102752675258e72ba447982c65e010e1cfd29a", size = 2021446 }, + { url = "https://files.pythonhosted.org/packages/b2/3d/6f18d551b00bf8babaa3a569d5fd62cba2bd7bbdeaf82167a959352ba56b/torchtext-0.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4bfe9cb7b08cf7ff3473309d9f24ed243c3a847bfbb2c932925551bf7a05892", size = 1949005 }, ] [[package]] @@ -3611,6 +3980,15 @@ torch = [ { name = "torch" }, ] +[[package]] +name = "triton" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/22/507b6f58a35e05e84381630b2dc2a3cee1a7a2a7eaf4cba857c638a18a24/triton-3.5.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f90de6a6566bb619b4c0adc9855729e1b1b5e26533fca1bf6206e96b6d277a3", size = 159827599 }, + { url = "https://files.pythonhosted.org/packages/0b/eb/09e31d107a5d00eb281aa7e6635ca463e9bca86515944e399480eadb71f8/triton-3.5.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5d3b3d480debf24eaa739623c9a42446b0b77f95593d30eb1f64cd2278cc1f0", size = 170333110 }, +] + [[package]] name = "typer" version = "0.15.1" @@ -3731,6 +4109,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c", size = 3800523 }, ] +[[package]] +name = "validators" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712 }, +] + [[package]] name = "virtualenv" version = "20.29.1" @@ -3745,6 +4132,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/9b/599bcfc7064fbe5740919e78c5df18e5dceb0887e676256a1061bb5ae232/virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779", size = 4282379 }, ] +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, +] + [[package]] name = "watchfiles" version = "1.0.4" @@ -3977,3 +4382,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/b7/2e9a5b18eb0fe24c3a0e8bae994e812ed9852ab4fd067c0107fadde0d5f0/yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", size = 90484 }, { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 }, ] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, +] From fe35a4edbe2a7fa9de3ce9c37e9b34fc05df1562 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 10 Nov 2025 21:35:28 +0000 Subject: [PATCH 006/101] =?UTF-8?q?=F0=9F=9A=A7=20=20wip:=20Update=20extra?= =?UTF-8?q?ction=20examples=20in=20langextract=20notebook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../anonymization/05-langextract.ipynb | 125 +++++++++++++----- 1 file changed, 95 insertions(+), 30 deletions(-) diff --git a/notebooks/experiments/anonymization/05-langextract.ipynb b/notebooks/experiments/anonymization/05-langextract.ipynb index 0f7ab234..c7035718 100644 --- a/notebooks/experiments/anonymization/05-langextract.ipynb +++ b/notebooks/experiments/anonymization/05-langextract.ipynb @@ -63,23 +63,17 @@ " lx.data.Extraction(\n", " extraction_class=\"FECHA\", extraction_text=\"5 de mayo de 2023\"\n", " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Juan Pérez\"\n", - " ),\n", + " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Juan Pérez\"),\n", " lx.data.Extraction(\n", " extraction_class=\"DIRECCION\", extraction_text=\"Sarmiento 1234\"\n", " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"LOC\", extraction_text=\"Moreno\"\n", - " ),\n", + " lx.data.Extraction(extraction_class=\"LOC\", extraction_text=\"Moreno\"),\n", " ],\n", " ),\n", " lx.data.ExampleData(\n", " text=\"JUZGADO DE 1RA INSTANCIA EN LO PENAL CONTRAVENCIONAL Y DE FALTAS N°10 SECRETARIA N°19\\nCarlos Gómez sobre 84 - HOMICIDIO CULPOSO Y OTROS\\nNúmero: 52345/2022\\nCUIJ: 12-34567890-1\\nActuación Nro: 2022-009876\",\n", " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Carlos Gómez\"\n", - " ),\n", + " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Carlos Gómez\"),\n", " lx.data.Extraction(\n", " extraction_class=\"NUM_EXPEDIENTE\", extraction_text=\"52345/2022\"\n", " ),\n", @@ -94,15 +88,9 @@ " lx.data.ExampleData(\n", " text=\"Acusado: Miguel Torres, DNI 30123456, nacido el 14/02/1990, de 34 años de edad, de nacionalidad paraguaya, género varón cis, con estudios secundarios completos, hizo hasta 3er año porque fue padre joven, con último domicilio en Av. Corrientes 3456, de esta ciudad, donde vive con su hermana y su cuñado Jorge Pérez. Tiene dos hijos a su cargo, de 5 y 8 años. Su hijo de 8 vive con él, su hija de 5 vive con su madre, Laura Fernández.\",\n", " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Miguel Torres\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"DNI\", extraction_text=\"30123456\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"FECHA\", extraction_text=\"14/02/1990\"\n", - " ),\n", + " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Miguel Torres\"),\n", + " lx.data.Extraction(extraction_class=\"DNI\", extraction_text=\"30123456\"),\n", + " lx.data.Extraction(extraction_class=\"FECHA\", extraction_text=\"14/02/1990\"),\n", " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"34\"),\n", " lx.data.Extraction(\n", " extraction_class=\"NACIONALIDAD\", extraction_text=\"paraguaya\"\n", @@ -115,9 +103,7 @@ " extraction_class=\"DIRECCION\",\n", " extraction_text=\"Av. Corrientes 3456\",\n", " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Jorge Pérez\"\n", - " ),\n", + " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Jorge Pérez\"),\n", " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"5\"),\n", " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"8\"),\n", " lx.data.Extraction(\n", @@ -128,15 +114,11 @@ " lx.data.ExampleData(\n", " text=\"El testigo Juan López dejó asentado su número de contacto: 11-2345-6789. Indicó que la médica Dra. Ana García, MN 12345, asistió al lugar donde se hallaba un vehículo Volkswagen, patente AB123CD.\",\n", " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Juan López\"\n", - " ),\n", + " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Juan López\"),\n", " lx.data.Extraction(\n", " extraction_class=\"TELEFONO\", extraction_text=\"11-2345-6789\"\n", " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Ana García\"\n", - " ),\n", + " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Ana García\"),\n", " lx.data.Extraction(\n", " extraction_class=\"NUM_MATRICULA\", extraction_text=\"12345\"\n", " ),\n", @@ -180,9 +162,90 @@ " ),\n", " ],\n", " ),\n", + " lx.data.ExampleData(\n", + " text=\"En la Ciudad Autónoma de Buenos Aires, el día 5 de mayo de 2023, el Sr. Fiscal hace saber que Juan Pérez se domicilia en la calle Sarmiento 1234, localidad de Moreno.\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"FECHA\", extraction_text=\"5 de mayo de 2023\"\n", + " ),\n", + " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Juan Pérez\"),\n", + " lx.data.Extraction(\n", + " extraction_class=\"DIRECCION\", extraction_text=\"Sarmiento 1234\"\n", + " ),\n", + " lx.data.Extraction(extraction_class=\"LOC\", extraction_text=\"Moreno\"),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"Por otra parte, la División Investigaciones Judiciales de la Policía Federal Argentina informó que no se dio intervención a ninguna otra Fiscalía u otro Juzgado por la sustracción del vehículo Volkswagen Voyage, dominio KXY-876\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"PATENTE_DOMINIO\", extraction_text=\"KXY-876\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"MARCA_AUTOMOVIL\",\n", + " extraction_text=\"Volkswagen Voyage\",\n", + " ),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"A su vez, requirió informes al Banco BBVA Francés, respecto de las cuentas bancarias de la denunciante, Carla Alejandra Garcia, D.N.I. 36.998.621 identificadas como Caja de ahorro en pesos argentinos número 117-59824/6 con CBU 0180132640000004685591 y Caja de ahorro en dólares número 119-619018/2 con CBU 0170115544000062081822.\",\n", + " extractions=[\n", + " lx.data.Extraction(\n", + " extraction_class=\"PER\", extraction_text=\"Carla Alejandra Garcia\"\n", + " ),\n", + " lx.data.Extraction(extraction_class=\"DNI\", extraction_text=\"36.998.621\"),\n", + " lx.data.Extraction(\n", + " extraction_class=\"NUM_CAJA_AHORRO\", extraction_text=\"117-59824/6\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"CBU\", extraction_text=\"0180132640000004685591\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"NUM_CAJA_AHORRO\", extraction_text=\"119-619018/2\"\n", + " ),\n", + " lx.data.Extraction(\n", + " extraction_class=\"CBU\", extraction_text=\"0170115544000062081822\"\n", + " ),\n", + " ],\n", + " ),\n", + " lx.data.ExampleData(\n", + " text=\"VISTOS: Que a fin de ordenar la marcha del proceso, se fija audiencia preliminar. No se consignan números de expediente, CUIJ ni domicilios en el presente proveído.\",\n", + " extractions=[], # ejemplo negativo: desalienta devolver clases ausentes\n", + " ),\n", "]" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb82c82d", + "metadata": {}, + "outputs": [], + "source": [ + "len(examples)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c52e56f4", + "metadata": {}, + "outputs": [], + "source": [ + "iter_examples = iter(examples)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68945d23", + "metadata": {}, + "outputs": [], + "source": [ + "example = next(iter_examples)\n", + "pprint(example)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -190,6 +253,7 @@ "metadata": {}, "outputs": [], "source": [ + "# Sample text to extract from\n", "text = \"La Fiscalía determinó que el objeto de este caso es investigar el hecho que tuvo lugar el día 12 de marzo de 2023 a las 8:50 horas aproximadamente, ocasión en que Carlos Gómez y María Rodriguez estafaron a Juan Pérez por un monto total de pesos treinta y tres mil novecientos sesenta ($33.960).\"\n", "\n", "# Run the extraction\n", @@ -197,8 +261,9 @@ " text_or_documents=text,\n", " prompt_description=prompt,\n", " examples=examples,\n", - " model_id=\"llama3.2:3b\",\n", + " model_id=\"llama3.1:8b\",\n", " model_url=\"http://localhost:11434\",\n", + " max_char_buffer=16_384,\n", " fence_output=False,\n", " use_schema_constraints=False,\n", ")\n", @@ -209,7 +274,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fb47da66", + "id": "82279f15", "metadata": {}, "outputs": [], "source": [] @@ -217,7 +282,7 @@ ], "metadata": { "kernelspec": { - "display_name": "aymurai (3.10.18)", + "display_name": "aymurai", "language": "python", "name": "python3" }, From 3d3d2300a5f2e1b06d5284507d16d87e02815fb7 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 14 Nov 2025 19:10:28 +0000 Subject: [PATCH 007/101] =?UTF-8?q?=F0=9F=93=9D=20=20Add=20entity=20disamb?= =?UTF-8?q?iguation=20notebook=20for=20canonical=20entity=20extraction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../01-entity-disambiguation.ipynb | 550 ++++++++++++++++++ 1 file changed, 550 insertions(+) create mode 100644 notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb diff --git a/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb new file mode 100644 index 00000000..99477159 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb @@ -0,0 +1,550 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c8fc128c", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8eeefe8b", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import mimetypes\n", + "import os\n", + "import re\n", + "import time\n", + "from pathlib import Path\n", + "from typing import Iterable\n", + "\n", + "import ollama\n", + "import requests\n", + "from IPython.display import Markdown\n", + "from ollama import ChatResponse\n", + "from pydantic import BaseModel\n", + "from pydantic.json_schema import JsonSchemaValue\n", + "from rich.pretty import pprint\n", + "from tqdm import tqdm\n", + "\n", + "from aymurai.meta.entities import CanonicalEntity # , EntityRelation\n", + "from aymurai.utils.json_data import get_pretty, load_json, save_json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46b328aa", + "metadata": {}, + "outputs": [], + "source": [ + "os.environ[\"OLLAMA_KEEP_ALIVE\"] = \"0\"\n", + "print(\"OLLAMA_KEEP_ALIVE set to 0. Models will unload immediately after use.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "758353c4", + "metadata": {}, + "outputs": [], + "source": [ + "DATA_DIR = \"/resources/data/restricted/summarization\"\n", + "API_URL = \"http://localhost:8899\" # Url for debugger. change it to your own" + ] + }, + { + "cell_type": "markdown", + "id": "84d11bba", + "metadata": {}, + "source": [ + "## /document-extract endpoint output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e0ec450", + "metadata": {}, + "outputs": [], + "source": [ + "BASE_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", + "ENDPOINT = f\"{BASE_URL}/misc/document-extract\"\n", + "DATA_ROOT = Path(\n", + " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef352767", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "print(f\"Discovered {len(documents)} documents.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21fd6e36", + "metadata": {}, + "outputs": [], + "source": [ + "def call_extraction_api(\n", + " session: requests.Session, file_path: Path\n", + ") -> dict[str, object]:\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " ENDPOINT,\n", + " files=files,\n", + " timeout=REQUEST_TIMEOUT_S,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload" + ] + }, + { + "cell_type": "markdown", + "id": "9254ef68", + "metadata": {}, + "source": [ + "## Inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a308cf90", + "metadata": {}, + "outputs": [], + "source": [ + "# Function to make inference using the API\n", + "def get_predictions(sample: str) -> dict:\n", + " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample})\n", + " response.raise_for_status()\n", + " return response.json()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ef6a0fa", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import chain\n", + "from operator import itemgetter\n", + "from typing import Any\n", + "\n", + "from more_itertools import unique_everseen\n", + "\n", + "\n", + "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", + " \"\"\"\n", + " Parse prediction labels to extract unique aymurai_label and aymurai_alt_text pairs.\n", + "\n", + " Args:\n", + " predictions (list[dict[str, Any]]): A list of prediction dictionaries.\n", + "\n", + " Returns:\n", + " list[dict[str, str]]: A list of dictionaries containing unique aymurai_label and aymurai_alt_text pairs.\n", + " \"\"\"\n", + " attrs_stream = (\n", + " label.get(\"attrs\") or {}\n", + " for label in chain.from_iterable(pred.get(\"labels\", ()) for pred in predictions)\n", + " )\n", + "\n", + " unique_pairs = unique_everseen(\n", + " (\n", + " attrs.get(\"aymurai_label\"),\n", + " attrs.get(\"aymurai_alt_text\"),\n", + " )\n", + " for attrs in attrs_stream\n", + " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", + " )\n", + "\n", + " return sorted(\n", + " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", + " key=itemgetter(\"aymurai_label\", \"text\"),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "ccbe21f4", + "metadata": {}, + "source": [ + "## CanonicalEntity extraction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e2a8113", + "metadata": {}, + "outputs": [], + "source": [ + "# Function to get chat response from the model\n", + "def get_chat_response(\n", + " user_prompt: str,\n", + " model: str = \"gpt-oss:20b\",\n", + " system_prompt: str = \"Sos un asistente jurídico que resume sin inventar nada.\",\n", + " options: dict = {\"temperature\": 0},\n", + " format: JsonSchemaValue | None = None,\n", + ") -> ChatResponse:\n", + " \"\"\"\n", + " Get chat response from the model.\n", + "\n", + " Args:\n", + " user_prompt (str): The prompt from the user.\n", + " model (str, optional): The model to use. Defaults to \"gpt-oss:20b\".\n", + " system_prompt (str, optional): The system prompt. Defaults to \"Sos un asistente jurídico que resume sin inventar nada.\".\n", + " options (dict, optional): The options for the chat. Defaults to {\"temperature\": 0}.\n", + " format (JsonSchemaValue | None, optional): The format for the response. Defaults to None.\n", + "\n", + " Returns:\n", + " ChatResponse: The response from the chat.\n", + " \"\"\"\n", + " response = ollama.chat(\n", + " model=model,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options=options,\n", + " format=format,\n", + " )\n", + "\n", + " return response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd025617", + "metadata": {}, + "outputs": [], + "source": [ + "# Sanity check\n", + "get_chat_response(\"hola, ¿cómo estás?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09631e51", + "metadata": {}, + "outputs": [], + "source": [ + "class CanonicalEntities(BaseModel):\n", + " canonical_entities: list[CanonicalEntity]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c7ec7c6", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = \"\"\"\n", + "Eres un asistente especializado en anonimización de sentencias judiciales.\n", + "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", + "Una **entidad canónica** es la representación única de una entidad real.\n", + "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", + "\n", + "# Reglas\n", + "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", + "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", + "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", + "- Cada entidad canónica debe incluir:\n", + " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", + " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", + " - `aliases` (todas las menciones textuales relevantes).\n", + " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", + "\n", + "# Notas\n", + "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", + "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + "\n", + "# Ejemplo\n", + "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", + "A: ```json\n", + "[\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Laura Beatriz Gómez\",\n", + " \"aliases\": [\"Laura Beatriz Gómez\"],\n", + " \"attributes\": {\"role\": \"Denunciante\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"42987654\",\n", + " \"aliases\": [\"42.987.654\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DIRECCION\",\n", + " \"canonical_text\": \"calle Falsa 123\",\n", + " \"aliases\": [\"calle Falsa 123\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", + " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", + " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", + " \"attributes\": {\"role\": \"Denunciado\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"27654321\",\n", + " \"aliases\": [\"27.654.321\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"calle Real 789\",\n", + " \"aliases\": [\"calle Real 789\"]\n", + " }\n", + "]\n", + "```\n", + "\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6994e3c3", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template = \"\"\"\n", + "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", + "\n", + "# Documento\n", + "{document_text}\n", + "\n", + "# Menciones de entidades detectadas por el NER\n", + "{ner_output_json}\n", + "\n", + "# Instrucciones\n", + "1. Evalúa cada mención listada. Si representa una entidad real, inclúyela en la lista de aliases de alguna entidad canónica.\n", + "2. No omitas entidades válidas, alias con iniciales ni variantes abreviadas.\n", + "3. Solo fusiona menciones cuando exista evidencia clara de que se refieren a la misma entidad.\n", + "4. Normaliza canonical_text; mantén los alias tal como aparecen.\n", + "5. Utiliza attributes para indicar roles (p. ej. {{\"role\": \"Denunciante\"}}) o aclaraciones de desambiguación.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "349fa489", + "metadata": {}, + "outputs": [], + "source": [ + "def extract_canonical_entities(\n", + " doc_path: str, model: str = \"phi4:14b\"\n", + ") -> CanonicalEntities:\n", + " \"\"\"\n", + " Extract canonical entities from a document.\n", + "\n", + " Args:\n", + " doc_path (str): The path to the document.\n", + " model (str, optional): The model to use for extraction. Defaults to \"phi4:14b\".\n", + "\n", + " Raises:\n", + " ValueError: If the document is empty or not found.\n", + " ValueError: If the document summary is empty or not found.\n", + "\n", + " Returns:\n", + " CanonicalEntities: The extracted canonical entities.\n", + " \"\"\"\n", + " # Extract document\n", + " session = requests.Session()\n", + " document = call_extraction_api(session, Path(doc_path))\n", + " document = document.get(\"detail\", {}).get(\"document\")\n", + "\n", + " if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")\n", + "\n", + " # Get NER predictions\n", + " ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + " parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", + " ner_output_json = get_pretty(parsed_ner_labels)\n", + "\n", + " # Prepare user prompt\n", + " user_prompt = user_prompt_template.format(\n", + " document_text=\"\\n\".join(document).strip(),\n", + " ner_output_json=ner_output_json,\n", + " )\n", + "\n", + " # Get chat response\n", + " response = get_chat_response(\n", + " user_prompt=user_prompt,\n", + " model=model,\n", + " system_prompt=system_prompt,\n", + " format=CanonicalEntities.model_json_schema(),\n", + " )\n", + "\n", + " # Parse canonical entities from response\n", + " canonical_entities = [\n", + " output for output in json.loads(response.message.content)[\"canonical_entities\"]\n", + " ]\n", + "\n", + " canonical_entities = [\n", + " CanonicalEntity.model_validate(canonical_entity)\n", + " for canonical_entity in canonical_entities\n", + " ]\n", + "\n", + " return canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52c14551", + "metadata": {}, + "outputs": [], + "source": [ + "for doc_path in documents:\n", + " print(f\"Processing document: {doc_path}\")\n", + "\n", + " try:\n", + " canonical_entities = extract_canonical_entities(doc_path)\n", + " print(f\"Extracted {len(canonical_entities)} canonical entities.\")\n", + "\n", + " target_filename = re.sub(\n", + " r\"\\s+\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", + " )\n", + " target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)\n", + " save_json(\n", + " [\n", + " canonical_entity.model_dump()\n", + " | {\"entity_id\": canonical_entity.entity_id.hex}\n", + " for canonical_entity in canonical_entities\n", + " ],\n", + " f\"entities-to-review/{target_filename}-canonical-entities.json\",\n", + " )\n", + "\n", + " except Exception as e:\n", + " print(f\"Error processing document {doc_path}: {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f734b642", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 444194bbfbb1534fe3c10e4239059ab199b4b5bc Mon Sep 17 00:00:00 2001 From: jansaldo Date: Tue, 18 Nov 2025 16:06:34 +0000 Subject: [PATCH 008/101] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20dependenc?= =?UTF-8?q?ies:=20langextract=20to=201.1.0=20and=20ollama=20to=200.6.1;=20?= =?UTF-8?q?add=20openai=20extra=20for=20langextract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 +- uv.lock | 186 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 175 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5e2711b4..0bd35304 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,8 +90,8 @@ dependencies = [ "pymupdf4llm>=0.0.17", "pypandoc>=1.15", "python-docx>=1.2.0", - "langextract==1.0.8", - "ollama==0.6.0", + "langextract[openai]==1.1.0", + "ollama==0.6.1", ] [project.urls] diff --git a/uv.lock b/uv.lock index d7646eee..7f198f0d 100644 --- a/uv.lock +++ b/uv.lock @@ -255,7 +255,7 @@ wheels = [ [[package]] name = "aymurai" -version = "1.1.13.dev5" +version = "1.1.13.dev16" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -269,7 +269,7 @@ dependencies = [ { name = "gdown" }, { name = "jiwer" }, { name = "joblib" }, - { name = "langextract" }, + { name = "langextract", extra = ["openai"] }, { name = "more-itertools" }, { name = "numpy" }, { name = "odfpy" }, @@ -325,11 +325,11 @@ requires-dist = [ { name = "gdown", specifier = "==4.6.0" }, { name = "jiwer", specifier = "==3.0.5" }, { name = "joblib", specifier = ">=1.4.2" }, - { name = "langextract", specifier = "==1.0.8" }, + { name = "langextract", extras = ["openai"], specifier = "==1.1.0" }, { name = "more-itertools", specifier = ">=10.5.0" }, { name = "numpy", specifier = "<2.0.0" }, { name = "odfpy", specifier = ">=1.4.1" }, - { name = "ollama", specifier = "==0.6.0" }, + { name = "ollama", specifier = "==0.6.1" }, { name = "psutil", specifier = "==6.1.0" }, { name = "pydantic", specifier = ">=2.10.4" }, { name = "pydantic-settings", specifier = ">=2.7.0" }, @@ -721,6 +721,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, ] +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + [[package]] name = "dnspython" version = "2.7.0" @@ -1059,6 +1068,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168 }, ] +[[package]] +name = "google-api-core" +version = "2.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706 }, +] + [[package]] name = "google-auth" version = "2.38.0" @@ -1086,9 +1111,55 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/0e/0636cc1448a7abc444fb1b3a63655e294e0d2d49092dc3de05241be6d43c/google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73", size = 18306 }, ] +[[package]] +name = "google-cloud-core" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469 }, +] + +[[package]] +name = "google-cloud-storage" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-cloud-core" }, + { name = "google-crc32c" }, + { name = "google-resumable-media" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/cd/7e112cf025b2b591067b599e4bfe965df0c12b0cc0afdb5556469bff126d/google_cloud_storage-3.6.0.tar.gz", hash = "sha256:29cc6b9a6c0fc9cdad071e375d540a5a50fbc9a7fad8300fa02fb904f6fe2ca2", size = 17251072 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/ef/3b57bf617ee0c79450c1ff211d1eb888db8fc1050ac74b3e52cc6ed86e63/google_cloud_storage-3.6.0-py3-none-any.whl", hash = "sha256:5decbdddd63b7d1fc3e266a393ad6453d2e27d172bd982b1e2f15481668db097", size = 299039 }, +] + +[[package]] +name = "google-crc32c" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/69/b1b05cf415df0d86691d6a8b4b7e60ab3a6fb6efb783ee5cd3ed1382bfd3/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76", size = 30467 }, + { url = "https://files.pythonhosted.org/packages/44/3d/92f8928ecd671bd5b071756596971c79d252d09b835cdca5a44177fa87aa/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d", size = 30311 }, + { url = "https://files.pythonhosted.org/packages/33/42/c2d15a73df79d45ed6b430b9e801d0bd8e28ac139a9012d7d58af50a385d/google_crc32c-1.7.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c67ca0a1f5b56162951a9dae987988679a7db682d6f97ce0f6381ebf0fbea4c", size = 37889 }, + { url = "https://files.pythonhosted.org/packages/57/ea/ac59c86a3c694afd117bb669bde32aaf17d0de4305d01d706495f09cbf19/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc5319db92daa516b653600794d5b9f9439a9a121f3e162f94b0e1891c7933cb", size = 33028 }, + { url = "https://files.pythonhosted.org/packages/60/44/87e77e8476767a4a93f6cf271157c6d948eacec63688c093580af13b04be/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcdf5a64adb747610140572ed18d011896e3b9ae5195f2514b7ff678c80f1603", size = 38026 }, + { url = "https://files.pythonhosted.org/packages/c8/bf/21ac7bb305cd7c1a6de9c52f71db0868e104a5b573a4977cd9d0ff830f82/google_crc32c-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:754561c6c66e89d55754106739e22fdaa93fafa8da7221b29c8b8e8270c6ec8a", size = 33476 }, + { url = "https://files.pythonhosted.org/packages/0b/43/31e57ce04530794917dfe25243860ec141de9fadf4aa9783dffe7dac7c39/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e9afc74168b0b2232fb32dd202c93e46b7d5e4bf03e66ba5dc273bb3559589", size = 28242 }, + { url = "https://files.pythonhosted.org/packages/eb/f3/8b84cd4e0ad111e63e30eb89453f8dd308e3ad36f42305cf8c202461cdf0/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa8136cc14dd27f34a3221c0f16fd42d8a40e4778273e61a3c19aedaa44daf6b", size = 28049 }, +] + [[package]] name = "google-genai" -version = "1.31.0" +version = "1.51.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1100,9 +1171,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/1b/da30fa6e2966942d7028a58eb7aa7d04544dcc3aa66194365b2e0adac570/google_genai-1.31.0.tar.gz", hash = "sha256:8572b47aa684357c3e5e10d290ec772c65414114939e3ad2955203e27cd2fcbc", size = 233482 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/1c/29245699c7c274ed5709b33b6a5192af2d57da5da3d2f189f222d1895336/google_genai-1.51.0.tar.gz", hash = "sha256:596c1ec964b70fec17a6ccfe6ee4edede31022584e8b1d33371d93037c4001b1", size = 258060 } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/27/1525bc9cbec58660f0842ebcbfe910a1dde908c2672373804879666e0bb8/google_genai-1.31.0-py3-none-any.whl", hash = "sha256:5c6959bcf862714e8ed0922db3aaf41885bacf6318751b3421bf1e459f78892f", size = 231876 }, + { url = "https://files.pythonhosted.org/packages/c6/28/0185dcda66f1994171067cfdb0e44a166450239d5b11b3a8a281dd2da459/google_genai-1.51.0-py3-none-any.whl", hash = "sha256:bfb7d0c6ba48ba9bda539f0d5e69dad827d8735a8b1e4703bafa0a2945d293e1", size = 260483 }, ] [[package]] @@ -1117,6 +1188,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a3/de/c648ef6835192e6e2cc03f40b19eeda4382c49b5bafb43d88b931c4c74ac/google_pasta-0.2.0-py3-none-any.whl", hash = "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed", size = 57471 }, ] +[[package]] +name = "google-resumable-media" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-crc32c" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340 }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/bc/cb5c74fca58d9c37bc621642e2c2b19c004d078b472d49fb03d9fa8ffeef/googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a", size = 121632 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/87/1608d23bb9879694579fff5dc56d60e3d48e012fd08670f140cf82f6cf26/googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877", size = 229151 }, +] + [[package]] name = "greenlet" version = "3.1.1" @@ -1386,6 +1481,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, ] +[[package]] +name = "jiter" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652 }, + { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829 }, + { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568 }, + { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052 }, + { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585 }, + { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541 }, + { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423 }, + { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958 }, + { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084 }, + { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054 }, + { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368 }, + { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847 }, + { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144 }, + { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877 }, + { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419 }, + { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212 }, + { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974 }, + { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233 }, + { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537 }, + { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110 }, +] + [[package]] name = "jiwer" version = "3.0.5" @@ -1747,13 +1870,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2 [[package]] name = "langextract" -version = "1.0.8" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "absl-py" }, { name = "aiohttp" }, { name = "async-timeout" }, { name = "exceptiongroup" }, + { name = "google-cloud-storage" }, { name = "google-genai" }, { name = "ml-collections" }, { name = "more-itertools" }, @@ -1766,9 +1890,14 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/d8/95666e87f02f3c83420ed030f14085c9c6acd986da336b514443dee6d649/langextract-1.0.8.tar.gz", hash = "sha256:dc4c65058969486faf119d2c0cd82a12ccdff1d9785212b96fd67c52cbbe74c7", size = 78418 } +sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/70/f5eba44ee7a8c8fe651b14585618a4ca600dd41a6fdc156b4377f4f53bb1/langextract-1.0.8-py3-none-any.whl", hash = "sha256:17b5985154ec1d58f5dd8fe4d6135d0cd4b4d650a075f8cf36eca705e2459263", size = 84987 }, + { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592 }, +] + +[package.optional-dependencies] +openai = [ + { name = "openai" }, ] [[package]] @@ -2346,15 +2475,34 @@ wheels = [ [[package]] name = "ollama" -version = "0.6.0" +version = "0.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/47/f9ee32467fe92744474a8c72e138113f3b529fc266eea76abfdec9a33f3b/ollama-0.6.0.tar.gz", hash = "sha256:da2b2d846b5944cfbcee1ca1e6ee0585f6c9d45a2fe9467cbcd096a37383da2f", size = 50811 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/c1/edc9f41b425ca40b26b7c104c5f6841a4537bb2552bfa6ca66e81405bb95/ollama-0.6.0-py3-none-any.whl", hash = "sha256:534511b3ccea2dff419ae06c3b58d7f217c55be7897c8ce5868dfb6b219cf7a0", size = 14130 }, + { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354 }, +] + +[[package]] +name = "openai" +version = "2.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/e4/42591e356f1d53c568418dc7e30dcda7be31dd5a4d570bca22acb0525862/openai-2.8.1.tar.gz", hash = "sha256:cb1b79eef6e809f6da326a7ef6038719e35aa944c42d081807bfa1be8060f15f", size = 602490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/4f/dbc0c124c40cb390508a82770fb9f6e3ed162560181a85089191a851c59a/openai-2.8.1-py3-none-any.whl", hash = "sha256:c6c3b5a04994734386e8dad3c00a393f56d3b68a27cd2e8acae91a59e4122463", size = 1022688 }, ] [[package]] @@ -2573,6 +2721,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 }, ] +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, +] + [[package]] name = "protobuf" version = "3.19.6" From 8b13aad4b71b0b397ab36af0e0d3be71d8f465f8 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Tue, 18 Nov 2025 16:06:51 +0000 Subject: [PATCH 009/101] =?UTF-8?q?=F0=9F=93=9D=20=20Integrate=20custom=20?= =?UTF-8?q?OpenAI=20model=20for=20extraction=20and=20remove=20failing=20em?= =?UTF-8?q?pty=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../anonymization/05-langextract.ipynb | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/notebooks/experiments/anonymization/05-langextract.ipynb b/notebooks/experiments/anonymization/05-langextract.ipynb index c7035718..df2b0646 100644 --- a/notebooks/experiments/anonymization/05-langextract.ipynb +++ b/notebooks/experiments/anonymization/05-langextract.ipynb @@ -208,10 +208,6 @@ " ),\n", " ],\n", " ),\n", - " lx.data.ExampleData(\n", - " text=\"VISTOS: Que a fin de ordenar la marcha del proceso, se fija audiencia preliminar. No se consignan números de expediente, CUIJ ni domicilios en el presente proveído.\",\n", - " extractions=[], # ejemplo negativo: desalienta devolver clases ausentes\n", - " ),\n", "]" ] }, @@ -274,7 +270,60 @@ { "cell_type": "code", "execution_count": null, - "id": "82279f15", + "id": "9064a3b6", + "metadata": {}, + "outputs": [], + "source": [ + "# Custom OpenAI model integration\n", + "# Support for Ollama gpt-oss:20b #116\n", + "# https://github.com/google/langextract/issues/116#issuecomment-3209618602\n", + "from langextract.providers.openai import OpenAILanguageModel\n", + "\n", + "\n", + "# Custom OpenAI model class to handle Ollama gpt-oss:20b\n", + "class CustomOpenAIModel(OpenAILanguageModel):\n", + " def _process_single_prompt(self, prompt, config):\n", + " api_params = {\n", + " \"model\": self.model_id,\n", + " \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n", + " \"temperature\": 0.1,\n", + " \"max_tokens\": 2000,\n", + " }\n", + "\n", + " try:\n", + " response = self._client.chat.completions.create(**api_params)\n", + " output_text = response.choices[0].message.content\n", + " return lx.inference.ScoredOutput(score=1.0, output=output_text)\n", + "\n", + " except Exception as e:\n", + " raise lx.exceptions.InferenceRuntimeError(\n", + " f\"Custom OpenAI API error: {str(e)}\", original=e\n", + " ) from e\n", + "\n", + "\n", + "# Instantiate the custom model\n", + "custom_model = CustomOpenAIModel(\n", + " model_id=\"gpt-oss:20b\",\n", + " api_key=\"dummy\",\n", + " base_url=\"http://localhost:11434/v1\",\n", + ")\n", + "\n", + "# Run extraction with the custom model\n", + "result = lx.extract(\n", + " text_or_documents=text,\n", + " prompt_description=prompt,\n", + " examples=examples,\n", + " model=custom_model,\n", + " fence_output=False,\n", + " use_schema_constraints=False,\n", + ")\n", + "pprint(result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e905e794", "metadata": {}, "outputs": [], "source": [] @@ -282,7 +331,7 @@ ], "metadata": { "kernelspec": { - "display_name": "aymurai", + "display_name": "aymurai (3.10.19)", "language": "python", "name": "python3" }, @@ -296,7 +345,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.18" + "version": "3.10.19" } }, "nbformat": 4, From 68eae78c29fa3ef67c2893c7ad0493c24e33d53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:06:56 -0300 Subject: [PATCH 010/101] =?UTF-8?q?=F0=9F=93=9D=20Update=20error=20message?= =?UTF-8?q?=20format=20in=20json=5Fserial=20function=20for=20better=20read?= =?UTF-8?q?ability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- aymurai/utils/json_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aymurai/utils/json_data.py b/aymurai/utils/json_data.py index ad43164e..f07e4e9f 100644 --- a/aymurai/utils/json_data.py +++ b/aymurai/utils/json_data.py @@ -20,7 +20,7 @@ def json_serial(obj: Any) -> str: """ # noqa: E501 if isinstance(obj, (datetime, date)): return obj.isoformat() - raise TypeError("Type %s not serializable" % type(obj)) + raise TypeError(f"Type {type(obj)} not serializable") def get_pretty(obj: dict | list[Any]) -> str: From 45176013d64b77d62019bbe72bbf3fa8c0613543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:11:11 -0300 Subject: [PATCH 011/101] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Inline=20immediate?= =?UTF-8?q?=20return=20in=20get=5Fpretty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- aymurai/utils/json_data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aymurai/utils/json_data.py b/aymurai/utils/json_data.py index f07e4e9f..58fbb2d4 100644 --- a/aymurai/utils/json_data.py +++ b/aymurai/utils/json_data.py @@ -33,8 +33,7 @@ def get_pretty(obj: dict | list[Any]) -> str: Returns: str: the pretty json string. """ - pretty_json_str = json.dumps(obj, indent=4, ensure_ascii=False, default=json_serial) - return pretty_json_str + return json.dumps(obj, indent=4, ensure_ascii=False, default=json_serial) def save_json(json_data: dict | list[dict], file_path: str) -> None: From e96d8e41ef871736f290bb6c8e9c00e5e29e5a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:14:09 -0300 Subject: [PATCH 012/101] =?UTF-8?q?=F0=9F=90=9B=20Fix:=20Use=20json=5Fseri?= =?UTF-8?q?al=20in=20save=5Fjson?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- aymurai/utils/json_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aymurai/utils/json_data.py b/aymurai/utils/json_data.py index 58fbb2d4..c8122114 100644 --- a/aymurai/utils/json_data.py +++ b/aymurai/utils/json_data.py @@ -46,7 +46,7 @@ def save_json(json_data: dict | list[dict], file_path: str) -> None: file_path (str): The path to the file where the JSON data will be saved. """ with open(file_path, "w") as f: - f.write(json.dumps(json_data, indent=4, ensure_ascii=False)) + f.write(json.dumps(json_data, indent=4, ensure_ascii=False, default=json_serial)) def load_json(json_file_path: str) -> dict | list[dict]: From c45a86348a89f6011756cc6316c04630b1318669 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Tue, 18 Nov 2025 17:17:46 +0000 Subject: [PATCH 013/101] =?UTF-8?q?=F0=9F=8E=A8=20Format=20json.dumps=20ca?= =?UTF-8?q?ll=20in=20save=5Fjson=20for=20improved=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/utils/json_data.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aymurai/utils/json_data.py b/aymurai/utils/json_data.py index c8122114..7fda48d0 100644 --- a/aymurai/utils/json_data.py +++ b/aymurai/utils/json_data.py @@ -46,7 +46,9 @@ def save_json(json_data: dict | list[dict], file_path: str) -> None: file_path (str): The path to the file where the JSON data will be saved. """ with open(file_path, "w") as f: - f.write(json.dumps(json_data, indent=4, ensure_ascii=False, default=json_serial)) + f.write( + json.dumps(json_data, indent=4, ensure_ascii=False, default=json_serial) + ) def load_json(json_file_path: str) -> dict | list[dict]: From 401a08b2ebaa0b2e40a34590b4976f9da5c70150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Wed, 19 Nov 2025 17:57:56 -0300 Subject: [PATCH 014/101] Feature/ollama service (#59) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add GPU-enabled Ollama service to compose stack * 🔧 Add Make targets for managing Ollama service and models * 🔧 Add launch configuration and task for starting Ollama service --- .vscode/launch.json | 3 ++- .vscode/tasks.json | 19 +++++++++++++++++++ Makefile | 34 ++++++++++++++++++++++++++++++++++ docker-compose.yml | 23 +++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 00fd92d6..d6348f6f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,7 +19,8 @@ ], "subProcess": false, "envFile": "${workspaceFolder}/.env", - "python": "${workspaceFolder}/.venv/bin/python" + "python": "${workspaceFolder}/.venv/bin/python", + "preLaunchTask": "Start Ollama service" }, ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..6283a7ed --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,19 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Start Ollama service", + "type": "shell", + "command": "make", + "args": [ + "ollama-up" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile index 64e27084..2f8a00e5 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,40 @@ api-full-run: api-full-pull: docker compose pull aymurai-api-full +ollama-up: + docker compose up -d --no-recreate ollama + +ollama-stop: + docker compose stop ollama + +ollama-restart: + docker compose restart ollama + +ollama-pull: +ifndef MODEL + $(error MODEL variable is required, e.g. make ollama-pull MODEL=llama3) +endif + docker compose up -d --no-recreate ollama + docker compose exec ollama ollama pull $(MODEL) + +ollama-run: +ifndef MODEL + $(error MODEL variable is required, e.g. make ollama-run MODEL=llama3) +endif + docker compose up -d --no-recreate ollama + docker compose exec -it ollama ollama run $(MODEL) + +ollama-list: + docker compose up -d --no-recreate ollama + docker compose exec ollama ollama list + +ollama-rm: +ifndef MODEL + $(error MODEL variable is required, e.g. make ollama-rm MODEL=llama3) +endif + docker compose up -d --no-recreate ollama + docker compose exec ollama ollama rm $(MODEL) + stress-test: locust -f locustfile.py --host http://localhost:8899 diff --git a/docker-compose.yml b/docker-compose.yml index 3ce7c536..b9f2ab12 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,8 @@ services: image: ghcr.io/aymurai/api:latest ports: - "8899:8899" + depends_on: + - ollama build: context: . dockerfile: ./docker/api/Dockerfile @@ -17,6 +19,8 @@ services: image: ghcr.io/aymurai/api:full ports: - "8899:8899" + depends_on: + - ollama build: context: . dockerfile: ./docker/api/Dockerfile @@ -27,3 +31,22 @@ services: limits: memory: 4g cpus: "4.0" + + ollama: + image: ollama/ollama + container_name: ollama + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] + ports: + - "11434:11434" + volumes: + - ollama:/root/.ollama + restart: always + +volumes: + ollama: From 29d83280f3c56cd5a551339a93f79899fe2a145d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Thu, 20 Nov 2025 20:13:47 -0300 Subject: [PATCH 015/101] Feature/llm providers (#60) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add GPU-enabled Ollama service to compose stack * 🔧 Add Make targets for managing Ollama service and models * 🔧 Add launch configuration and task for starting Ollama service * ✨ Implement LLM providers module with Ollama adapter and shared abstractions * ✅ Add unit tests for LLM providers including DummyProvider and OllamaLLMProvider * 📝 Document Ollama provider usage via notebook demo * 🐛 Fix tokenizer encoding by removing unnecessary special tokens flag * ♻️ Refactor chunk handling in LLMProvider to use _append_chunk method for consistency and improved readability * ✨ Enhance Ollama provider docs and DRY response building for sync/async calls * ♻️ Refactor OllamaLLMProvider to reuse AsyncClient instance for improved efficiency * 📝 Add async examples to OllamaLLMProvider notebook * ✅ Add async coverage for OllamaLLMProvider and tighten chunking tests * ♻️ Refactor OllamaLLMProvider to remove async client caching and streamline client instantiation --- aymurai/llm_providers/__init__.py | 9 + aymurai/llm_providers/ollama_provider.py | 254 +++++++++++++++++ aymurai/llm_providers/provider.py | 256 ++++++++++++++++++ .../llm-providers/01-ollama-provider.ipynb | 200 ++++++++++++++ test/llm_providers/__init__.py | 0 test/llm_providers/test_providers.py | 148 ++++++++++ 6 files changed, 867 insertions(+) create mode 100644 aymurai/llm_providers/__init__.py create mode 100644 aymurai/llm_providers/ollama_provider.py create mode 100644 aymurai/llm_providers/provider.py create mode 100644 notebooks/experiments/llm-providers/01-ollama-provider.ipynb create mode 100644 test/llm_providers/__init__.py create mode 100644 test/llm_providers/test_providers.py diff --git a/aymurai/llm_providers/__init__.py b/aymurai/llm_providers/__init__.py new file mode 100644 index 00000000..c63aaaac --- /dev/null +++ b/aymurai/llm_providers/__init__.py @@ -0,0 +1,9 @@ +from aymurai.llm_providers.ollama_provider import OllamaLLMProvider +from aymurai.llm_providers.provider import DocumentChunk, LLMProvider, LLMResponse + +__all__ = [ + "DocumentChunk", + "LLMProvider", + "LLMResponse", + "OllamaLLMProvider", +] diff --git a/aymurai/llm_providers/ollama_provider.py b/aymurai/llm_providers/ollama_provider.py new file mode 100644 index 00000000..b3e9ef80 --- /dev/null +++ b/aymurai/llm_providers/ollama_provider.py @@ -0,0 +1,254 @@ +from __future__ import annotations + +from typing import Any, AsyncIterator, Iterator + +import ollama +from ollama import AsyncClient + +from aymurai.llm_providers.provider import LLMProvider, LLMResponse + + +class OllamaLLMProvider(LLMProvider): + """Adapter that wraps `ollama.chat` preserving the common interface.""" + + def __init__( + self, + model: str, + *, + system_prompt: str | None = None, + keep_alive: int | str | None = None, + **kwargs, + ) -> None: + super().__init__(model=model, **kwargs) + self.system_prompt = system_prompt + self.keep_alive = keep_alive + + def generate( + self, + prompt: str | None = None, + *, + messages: list[dict[str, str]] | None = None, + **kwargs, + ) -> LLMResponse: + """ + Generate text using the configured Ollama model. + + Args: + prompt (str | None, optional): Optional single prompt string to be transformed into a chat message. Defaults to None. + messages (list[dict[str, str]] | None, optional): Optional list of pre-formatted chat messages to send. Defaults to None. + + Returns: + LLMResponse: Response containing the generated text, metadata about the request, and the raw payload. + """ + # Build the message payload + payload = self._build_messages(prompt=prompt, messages=messages) + + # Call ollama.chat + response = ollama.chat( + model=self.model_name, + messages=payload, + keep_alive=self.keep_alive, + **kwargs, + ) + + return self._build_llm_response( + response, + extra_metadata={ + "eval_count": response.get("eval_count"), + "eval_duration": response.get("eval_duration"), + }, + ) + + async def async_generate( + self, + prompt: str | None = None, + *, + messages: list[dict[str, str]] | None = None, + **kwargs, + ) -> LLMResponse: + """ + Asynchronously generate text using the configured Ollama model. + + Args: + prompt (str | None): Optional single prompt string to be transformed into a chat message. Defaults to None. + messages (list[dict[str, str]] | None): Optional list of pre-formatted chat messages to send. Defaults to None. + **kwargs: Additional keyword arguments forwarded to the Ollama AsyncClient chat endpoint. + + Returns: + LLMResponse: Response containing the generated text, metadata about the request, and the raw payload. + """ + # Build the message payload + payload = self._build_messages(prompt=prompt, messages=messages) + + # Call ollama.chat asynchronously + client = self._get_async_client() + response = await client.chat( + model=self.model_name, + messages=payload, + keep_alive=self.keep_alive, + **kwargs, + ) + + return self._build_llm_response( + response, + extra_metadata={ + "eval_count": response.get("eval_count"), + "eval_duration": response.get("eval_duration"), + }, + ) + + def stream( + self, + prompt: str | None = None, + *, + messages: list[dict[str, str]] | None = None, + **kwargs, + ) -> Iterator[LLMResponse]: + """ + Stream the response from the model using the provided prompt or messages. + + Args: + prompt (str | None, optional): Optional single prompt string to be transformed into a chat message. Defaults to None. + messages (list[dict[str, str]] | None, optional): Optional list of pre-formatted chat messages to send. Defaults to None. + + Yields: + Iterator[LLMResponse]: Response chunks containing generated text, metadata, and raw payloads. + """ + # Build the message payload + payload = self._build_messages(prompt=prompt, messages=messages) + + # Call ollama.chat with streaming enabled + stream_kwargs = {**kwargs, "stream": True} + + # Iterate over the streamed responses + for chunk in ollama.chat( + model=self.model_name, + messages=payload, + keep_alive=self.keep_alive, + **stream_kwargs, + ): + yield self._build_stream_response(chunk) + + async def async_stream( + self, + prompt: str | None = None, + *, + messages: list[dict[str, str]] | None = None, + **kwargs, + ) -> AsyncIterator[LLMResponse]: + """ + Asynchronously stream the response from the model using the provided prompt or messages. + + Args: + prompt (str | None, optional): Optional single prompt string to be transformed into a chat message. Defaults to None. + messages (list[dict[str, str]] | None, optional): Optional list of pre-formatted chat messages to send. Defaults to None. + + Yields: + AsyncIterator[LLMResponse]: Asynchronous iterator yielding response chunks containing generated text, metadata, and raw payloads. + """ + # Build the message payload + payload = self._build_messages(prompt=prompt, messages=messages) + + # Call ollama.chat with streaming enabled + stream_kwargs = {**kwargs, "stream": True} + + # Iterate over the streamed responses asynchronously + client = self._get_async_client() + stream = await client.chat( + model=self.model_name, + messages=payload, + keep_alive=self.keep_alive, + **stream_kwargs, + ) + async for chunk in stream: + yield self._build_stream_response(chunk) + + def _build_messages( + self, + *, + prompt: str | None, + messages: list[dict[str, str]] | None = None, + ) -> list[dict[str, str]]: + """ + Build the message payload for `ollama.chat`. + + Args: + prompt (str | None): The user prompt to be sent to the model. + messages (list[dict[str, str]] | None, optional): A list of message dictionaries to be sent to the model. + Defaults to None. + + Raises: + ValueError: If neither prompt nor messages are provided. + + Returns: + list[dict[str, str]]: The message payload to be sent to `ollama.chat`. + """ + if messages is not None: + return list(messages) + + if prompt is None: + raise ValueError("Either prompt or messages must be provided.") + + payload = [] + + if self.system_prompt: + payload.append({"role": "system", "content": self.system_prompt}) + + payload.append({"role": "user", "content": prompt}) + + return payload + + def _get_async_client(self) -> AsyncClient: + """ + Create a fresh AsyncClient instance. + + Returns: + AsyncClient: A new AsyncClient instance for making asynchronous requests. + """ + return AsyncClient() + + def _build_llm_response( + self, response: dict[str, Any], *, extra_metadata: dict[str, Any] | None = None + ) -> LLMResponse: + """ + Build an LLMResponse with consistent metadata from the response payload. + + Args: + response (dict[str, Any]): The response payload from the Ollama model. + extra_metadata (dict[str, Any] | None, optional): Additional metadata to include in the response. Defaults to None. + + Returns: + LLMResponse: The constructed LLMResponse object containing text, metadata, and raw response. + """ + # Extract the generated text + text = response.get("message", {}).get("content", "") + + # Build metadata + metadata = { + "model": self.model_name, + "provider": "ollama", + } + + # Include any extra metadata + if extra_metadata: + metadata.update(extra_metadata) + + return LLMResponse(text=text, metadata=metadata, raw=response) + + def _build_stream_response(self, chunk: dict[str, Any]) -> LLMResponse: + """ + Build stream responses with consistent metadata from chunk payloads. + + Args: + chunk (dict[str, Any]): The chunk payload from the Ollama model. + + Returns: + LLMResponse: The constructed LLMResponse object containing text, metadata, and raw chunk. + """ + return self._build_llm_response( + chunk, + extra_metadata={"done": chunk.get("done")}, + ) + + +__all__ = ["OllamaLLMProvider"] diff --git a/aymurai/llm_providers/provider.py b/aymurai/llm_providers/provider.py new file mode 100644 index 00000000..89e3750d --- /dev/null +++ b/aymurai/llm_providers/provider.py @@ -0,0 +1,256 @@ +from __future__ import annotations + +import abc +from typing import Any, Sequence + +from pydantic import BaseModel, Field + + +class LLMResponse(BaseModel): + """Standard response wrapper for any LLM provider.""" + + text: str + metadata: dict[str, Any] = Field(default_factory=dict) + raw: Any | None = None + + +class DocumentChunk(BaseModel): + """Represents a chunked portion of a larger document.""" + + text: str + token_count: int + index: int + + +TokenizerType = Any + + +class LLMProvider(abc.ABC): + """Base class with shared utilities for concrete LLM providers.""" + + def __init__( + self, + model: str, + *, + tokenizer: TokenizerType | None = None, + max_context_tokens: int | None = None, + chunk_overlap: int = 0, + ) -> None: + self.model_name = model + self._tokenizer = tokenizer + self.max_context_tokens = max_context_tokens + self.chunk_overlap = max(chunk_overlap, 0) + + @abc.abstractmethod + def generate(self, prompt: str, **kwargs) -> LLMResponse: + """ + Generate a response from the model using the provided prompt. + + Args: + prompt (str): The input prompt to generate a response for. + **kwargs: Additional provider-specific parameters. + + Returns: + LLMResponse: The generated response wrapped in an LLMResponse object. + """ + pass + + @abc.abstractmethod + def stream(self, prompt: str, **kwargs): + """ + Stream the response from the model using the provided prompt. + + Args: + prompt (str): The input prompt to generate a response for. + **kwargs: Additional provider-specific parameters. + + Raises: + NotImplementedError: If the provider does not support streaming. + """ + raise NotImplementedError("This provider does not support streaming.") + + def count_tokens(self, text: str) -> int: + """ + Count the number of tokens in the given text. + + Args: + text (str): The input text to count tokens for. + + Returns: + int: The number of tokens in the input text. + """ + tokens = self._tokenize(text) + return len(tokens) + + def chunk_text( + self, + text: str, + *, + max_tokens: int | None = None, + overlap: int | None = None, + ) -> list[DocumentChunk]: + """ + Chunk text ensuring every piece respects the token budget. + + Args: + text (str): The input text to chunk. + max_tokens (int | None): Optional maximum tokens per chunk. + overlap (int | None): Optional token overlap between chunks. + + Returns: + list[DocumentChunk]: A list of document chunks. + """ + cleaned_text = text.strip() + if not cleaned_text: + return [] + + limit = max_tokens or self.max_context_tokens + if limit is None: + total_tokens = self.count_tokens(cleaned_text) + return [DocumentChunk(text=cleaned_text, token_count=total_tokens, index=0)] + + words = cleaned_text.split() + if not words: + return [] + + chunks = [] + overlap_tokens = overlap if overlap is not None else self.chunk_overlap + current_words = [] + current_tokens = 0 + + for word in words: + if not current_words: + current_words = [word] + current_tokens = self.count_tokens(" ".join(current_words)) + if current_tokens > limit: + self._append_chunk( + chunks=chunks, + text=current_words[0], + token_count=current_tokens, + ) + current_words = [] + current_tokens = 0 + continue + + tentative_words = current_words + [word] + tentative_text = " ".join(tentative_words) + tentative_tokens = self.count_tokens(tentative_text) + + if tentative_tokens <= limit: + current_words = tentative_words + current_tokens = tentative_tokens + continue + + chunk_text = " ".join(current_words) + self._append_chunk( + chunks=chunks, + text=chunk_text, + token_count=current_tokens, + ) + + carry_over = self._overlap_tail(current_words, overlap_tokens) + current_words = carry_over + [word] + current_tokens = self.count_tokens(" ".join(current_words)) + + if current_tokens > limit: + chunk_text = " ".join(current_words) + self._append_chunk( + chunks=chunks, + text=chunk_text, + token_count=current_tokens, + ) + current_words = [] + current_tokens = 0 + + if current_words: + chunk_text = " ".join(current_words) + self._append_chunk( + chunks=chunks, + text=chunk_text, + token_count=current_tokens, + fallback_count=True, + ) + + return chunks + + def _tokenize(self, text: str) -> Sequence[Any]: + """ + Tokenize text using the configured tokenizer or fallback to whitespace. + + Args: + text (str): The input text to tokenize. + + Returns: + Sequence[Any]: The sequence of tokens. + """ + if self._tokenizer is None: + return text.split() + + tokenizer = self._tokenizer + if hasattr(tokenizer, "encode"): + return tokenizer.encode(text) + + if callable(tokenizer): + tokens = tokenizer(text) + if isinstance(tokens, dict): + return tokens.get("input_ids", []) + return tokens + + return text.split() + + def _overlap_tail(self, words: list[str], overlap_tokens: int) -> list[str]: + """ + Get the tail overlap of words based on the token budget. + + Args: + words (list[str]): The list of words to consider for overlap. + overlap_tokens (int): The token budget for the overlap. Must be non-negative. + + Returns: + list[str]: The list of words representing the tail overlap. + """ + assert overlap_tokens >= 0, "`overlap_tokens` must be non-negative." + + if not words or overlap_tokens == 0: + return [] + + tail = [] + + for word in reversed(words): + tail.insert(0, word) + token_budget = self.count_tokens(" ".join(tail)) + if token_budget >= overlap_tokens: + break + + return tail + + def _append_chunk( + self, + *, + chunks: list[DocumentChunk], + text: str, + token_count: int, + fallback_count: bool = False, + ) -> None: + """ + Append a chunk with consistent indexing, optionally recomputing token count if missing. + + Args: + chunks (list[DocumentChunk]): Accumulated chunk list. + text (str): Chunk text. + token_count (int): Token count for the chunk. + fallback_count (bool): Recompute token count when provided count is falsy. Defaults to False. + """ + count = token_count or ( + self.count_tokens(text) if fallback_count else token_count + ) + chunks.append( + DocumentChunk( + text=text, + token_count=count, + index=len(chunks), + ) + ) + + +__all__ = ["DocumentChunk", "LLMProvider", "LLMResponse"] diff --git a/notebooks/experiments/llm-providers/01-ollama-provider.ipynb b/notebooks/experiments/llm-providers/01-ollama-provider.ipynb new file mode 100644 index 00000000..216292c4 --- /dev/null +++ b/notebooks/experiments/llm-providers/01-ollama-provider.ipynb @@ -0,0 +1,200 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dcfd3a57", + "metadata": {}, + "source": [ + "# `OllamaLLMProvider`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9890c8fb", + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "\n", + "from rich.pretty import pprint\n", + "\n", + "from aymurai.llm_providers import OllamaLLMProvider" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "85317574", + "metadata": {}, + "outputs": [], + "source": [ + "MODEL_NAME = \"llama3.2:3b\"\n", + "SYSTEM_PROMPT = \"Eres un asistente legal especializado en anonimización y desambiguación de entidades.\"\n", + "\n", + "provider = OllamaLLMProvider(model=MODEL_NAME, system_prompt=SYSTEM_PROMPT)\n", + "provider" + ] + }, + { + "cell_type": "markdown", + "id": "94e43f16", + "metadata": {}, + "source": [ + "## Chunking" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53afac4f", + "metadata": {}, + "outputs": [], + "source": [ + "sample_text = (\n", + " \"La jueza fijó audiencia para la próxima semana y solicitó a las partes presentar \"\n", + " \"documentación adicional relacionada con la causa. El objetivo es determinar si existieron \"\n", + " \"violaciones al protocolo vigente y establecer medidas de protección para las víctimas.\"\n", + ")\n", + "\n", + "chunks = provider.chunk_text(sample_text, max_tokens=10, overlap=0)\n", + "for chunk in chunks:\n", + " print(f\"Chunk {chunk.index}: {chunk.token_count} tokens\")\n", + " pprint(chunk)" + ] + }, + { + "cell_type": "markdown", + "id": "1aa4d182", + "metadata": {}, + "source": [ + "## Generation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1ce3b9b", + "metadata": {}, + "outputs": [], + "source": [ + "prompt = (\n", + " \"Resume en español el siguiente extracto de un expediente judicial y menciona los actores involucrados:\"\n", + " + sample_text\n", + ")\n", + "\n", + "try:\n", + " summary_response = provider.generate(prompt)\n", + " print(summary_response.text)\n", + " pprint(summary_response.metadata)\n", + "\n", + "except Exception as exc:\n", + " print(\"No se pudo contactar a Ollama:\", exc)" + ] + }, + { + "cell_type": "markdown", + "id": "da8f9c60", + "metadata": {}, + "source": [ + "## Streaming" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ac718bd", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " stream_prompt = \"Explica en detalle el procedimiento legal descrito.\"\n", + " pieces = []\n", + " for event in provider.stream(stream_prompt):\n", + " print(event.text, end=\"\", flush=True)\n", + " pieces.append(event.text)\n", + " print()\n", + " full_text = \"\".join(pieces)\n", + "\n", + "except Exception as exc:\n", + " print(\"La transmisión no está disponible:\", exc)" + ] + }, + { + "cell_type": "markdown", + "id": "async-section", + "metadata": {}, + "source": [ + "## Async calls" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "async-demo", + "metadata": {}, + "outputs": [], + "source": [ + "async def demo_async_generate():\n", + " try:\n", + " result = await provider.async_generate(\"Di un resumen breve del texto\")\n", + " print(result.text)\n", + " except Exception as exc:\n", + " print(\"No se pudo contactar a Ollama (async):\", exc)\n", + "\n", + "\n", + "await demo_async_generate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72c5c551", + "metadata": {}, + "outputs": [], + "source": [ + "async def demo_async_stream():\n", + " try:\n", + " parts = []\n", + " async for event in provider.async_stream(\"Explica el caso con más detalle\"):\n", + " print(event.text, end=\"\", flush=True)\n", + " parts.append(event.text)\n", + " print()\n", + " except Exception as exc:\n", + " print(\"La transmisión async no está disponible:\", exc)\n", + "\n", + "\n", + "await demo_async_stream()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67ede506", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/llm_providers/__init__.py b/test/llm_providers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/llm_providers/test_providers.py b/test/llm_providers/test_providers.py new file mode 100644 index 00000000..9fbc64ae --- /dev/null +++ b/test/llm_providers/test_providers.py @@ -0,0 +1,148 @@ +import unittest +from unittest.mock import AsyncMock, MagicMock, patch + +from aymurai.llm_providers import LLMProvider, LLMResponse, OllamaLLMProvider + + +class DummyProvider(LLMProvider): + def __init__(self, **kwargs): + super().__init__(model="dummy", **kwargs) + self.last_prompt = None + + def generate(self, prompt: str, **kwargs) -> LLMResponse: + self.last_prompt = prompt + return LLMResponse(text=f"echo::{prompt}") + + def stream(self, prompt, **kwargs): + return super().stream(prompt, **kwargs) + + +class FakeTokenizer: + def encode(self, text, add_special_tokens: bool = False): + return text.split() + + +class BaseProviderTests(unittest.TestCase): + def test_chunk_text_respects_token_limit(self): + provider = DummyProvider(max_context_tokens=4, chunk_overlap=1) + text = "uno dos tres cuatro cinco seis" + chunks = provider.chunk_text(text) + self.assertGreater(len(chunks), 1) + for i, chunk in enumerate(chunks): + self.assertEqual(chunk.index, i) + self.assertLessEqual(chunk.token_count, 4) + + def test_chunk_text_overlap_applied(self): + provider = DummyProvider(max_context_tokens=3, chunk_overlap=1) + text = "one two three four" + chunks = provider.chunk_text(text) + self.assertEqual([c.index for c in chunks], [0, 1]) + self.assertTrue(chunks[0].text.split()[-1] in chunks[1].text.split()) + + def test_chunk_text_fallback_count(self): + provider = DummyProvider(max_context_tokens=1) + provider._tokenizer = type("EmptyTok", (), {"encode": lambda self, t: []})() + chunks = provider.chunk_text("hello") + self.assertEqual(len(chunks), 1) + self.assertEqual(chunks[0].token_count, 0) + + +class OllamaProviderTests(unittest.TestCase): + def test_generate_builds_messages(self): + provider = OllamaLLMProvider(model="llama3", system_prompt="Sistema") + fake_response = {"message": {"content": "respuesta"}, "eval_count": 10} + with patch("aymurai.llm_providers.ollama_provider.ollama.chat") as mock_chat: + mock_chat.return_value = fake_response + response = provider.generate("Hola") + + self.assertEqual(response.text, "respuesta") + args, kwargs = mock_chat.call_args + self.assertEqual(kwargs["model"], "llama3") + self.assertEqual(kwargs["messages"][0]["role"], "system") + self.assertEqual(response.metadata.get("model"), "llama3") + self.assertEqual(response.metadata.get("provider"), "ollama") + self.assertIn("eval_count", response.metadata) + self.assertIn("eval_duration", response.metadata) + + def test_generate_forwards_keep_alive(self): + provider = OllamaLLMProvider(model="llama3", keep_alive=30) + fake_response = {"message": {"content": "ok"}} + with patch("aymurai.llm_providers.ollama_provider.ollama.chat") as mock_chat: + mock_chat.return_value = fake_response + _ = provider.generate("Hola") + _, kwargs = mock_chat.call_args + self.assertEqual(kwargs["keep_alive"], 30) + + def test_stream_yields_chunks(self): + provider = OllamaLLMProvider(model="llama3") + fake_chunks = iter( + [ + {"message": {"content": "Hola"}, "done": False}, + {"message": {"content": " Mundo"}, "done": True}, + ] + ) + with patch("aymurai.llm_providers.ollama_provider.ollama.chat") as mock_chat: + mock_chat.return_value = fake_chunks + pieces = list(provider.stream("Hola")) + + self.assertEqual([piece.text for piece in pieces], ["Hola", " Mundo"]) + _, kwargs = mock_chat.call_args + self.assertTrue(kwargs["stream"]) + self.assertEqual(pieces[-1].text, " Mundo") + self.assertTrue(pieces[-1].metadata.get("done")) + + def test_generate_raises_without_prompt_or_messages(self): + provider = OllamaLLMProvider(model="llama3") + with self.assertRaises(ValueError): + provider.generate(prompt=None, messages=None) + + +class AsyncOllamaProviderTests(unittest.IsolatedAsyncioTestCase): + async def test_async_generate_builds_messages(self): + provider = OllamaLLMProvider(model="llama3", system_prompt="Sistema") + fake_response = {"message": {"content": "respuesta"}, "eval_count": 10} + mock_client = MagicMock() + mock_client.chat = AsyncMock(return_value=fake_response) + + with patch( + "aymurai.llm_providers.ollama_provider.AsyncClient", + return_value=mock_client, + ): + response = await provider.async_generate("Hola") + + self.assertEqual(response.text, "respuesta") + mock_client.chat.assert_awaited_once() + _, kwargs = mock_client.chat.call_args + self.assertEqual(kwargs["model"], "llama3") + self.assertEqual(kwargs["messages"][0]["role"], "system") + self.assertEqual(response.metadata.get("model"), "llama3") + self.assertEqual(response.metadata.get("provider"), "ollama") + self.assertIn("eval_count", response.metadata) + + async def test_async_stream_yields_chunks(self): + provider = OllamaLLMProvider(model="llama3") + + async def fake_gen(): + yield {"message": {"content": "Hola"}, "done": False} + yield {"message": {"content": " Mundo"}, "done": True} + + mock_client = MagicMock() + mock_client.chat = AsyncMock(return_value=fake_gen()) + + with patch( + "aymurai.llm_providers.ollama_provider.AsyncClient", + return_value=mock_client, + ): + pieces = [] + last_chunk = None + async for chunk in provider.async_stream("Hola"): + pieces.append(chunk.text) + last_chunk = chunk + + self.assertEqual(pieces, ["Hola", " Mundo"]) + mock_client.chat.assert_awaited() + self.assertTrue(last_chunk.metadata.get("done")) + + +if __name__ == "__main__": + unittest.main() From d80f74bc4264f51017a7ebe240ceef4abe567824 Mon Sep 17 00:00:00 2001 From: Paolo Donizetti Date: Wed, 26 Nov 2025 16:18:01 -0300 Subject: [PATCH 016/101] Feature/disambiguation metric v2 (#62) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update .gitignore to exclude entity disambiguation experiment directories and modify Jupyter notebook execution counts and output handling * Refactor Makefile for improved service management and update .gitignore to exclude specific experiment directories. Add new Jupyter notebooks for entity disambiguation metrics and documentation. * Adjust example data for consistency in entity representation. * Refactor entity disambiguation notebooks to standardize attribute naming and improve metric evaluation. Update role attribute from 'rol' to 'role' for consistency across examples and documentation. Adjust evaluation function to return both score and metrics. * Add evaluation metrics for entity disambiguation - Introduced new metrics module for evaluating entity disambiguation performance, including functions for alias normalization, Jaccard similarity, and greedy matching. - Implemented main evaluation function to compute scores and metrics from gold and predicted entities. - Added Jupyter notebooks for practical examples and evaluation results, including normalized and non-normalized text evaluations. - Updated documentation to reflect changes in function signatures and outputs. * 🔧 Expand Makefile: add API management targets (api-run, api-stop, api-logs, api-full-run) for smoother service control * ♻️ Refactor metrics.py: clarify docstrings, align type hints, and polish logging * ✏️ Fix role attribute reference in evaluation metric documentation for consistency * 🔧 Add CanonicalEntities class to represent a collection of canonical entities * 📝 Update entity disambiguation notebooks: clean up imports, adjust paths, and streamline API calls for improved clarity and functionality --------- Co-authored-by: padonizetti Co-authored-by: jansaldo --- Makefile | 19 +- aymurai/evaluation/__init__.py | 0 aymurai/evaluation/metrics.py | 366 +++++++++++++ aymurai/meta/entities.py | 6 + .../01-entity-disambiguation.ipynb | 130 ++--- .../02-disambiguation-metric.ipynb | 431 +++++++++++++++ .../03-disambiguation-evaluation.ipynb | 110 ++++ .../Desarrollo metrica.md | 500 ++++++++++++++++++ 8 files changed, 1472 insertions(+), 90 deletions(-) create mode 100644 aymurai/evaluation/__init__.py create mode 100644 aymurai/evaluation/metrics.py create mode 100644 notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb create mode 100644 notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb create mode 100644 notebooks/experiments/entity-disambiguation/Desarrollo metrica.md diff --git a/Makefile b/Makefile index 2f8a00e5..14e1d527 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,19 @@ include .env export $(shell sed 's/=.*//' .env) include .env.common export $(shell sed 's/=.*//' .env.common) +OLLAMA_CONTAINER := $(shell docker ps -q -f name=^ollama$$) api-build: docker compose build aymurai-api api-run: docker compose run --service-ports aymurai-api +api-up: + docker compose up -d aymurai-api +api-stop: + docker compose stop aymurai-api +api-logs: + docker compose logs -f aymurai-api api-pull: docker compose pull aymurai-api @@ -15,6 +22,12 @@ api-full-build: docker compose build aymurai-api-full api-full-run: docker compose run --service-ports aymurai-api-full +api-full-up: + docker compose up -d aymurai-api-full +api-full-stop: + docker compose stop aymurai-api-full +api-full-logs: + docker compose logs -f aymurai-api-full api-full-pull: docker compose pull aymurai-api-full @@ -31,8 +44,10 @@ ollama-pull: ifndef MODEL $(error MODEL variable is required, e.g. make ollama-pull MODEL=llama3) endif - docker compose up -d --no-recreate ollama - docker compose exec ollama ollama pull $(MODEL) +ifndef OLLAMA_CONTAINER + $(error Ollama container 'ollama' is not running. Start it first with 'make ollama-up') +endif + docker exec ollama ollama pull $(MODEL) ollama-run: ifndef MODEL diff --git a/aymurai/evaluation/__init__.py b/aymurai/evaluation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aymurai/evaluation/metrics.py b/aymurai/evaluation/metrics.py new file mode 100644 index 00000000..30bceca5 --- /dev/null +++ b/aymurai/evaluation/metrics.py @@ -0,0 +1,366 @@ +from __future__ import annotations + +import json +import unicodedata +from pathlib import Path +from typing import Any + +from aymurai.logger import get_logger +from aymurai.utils.json_data import load_json + +logger = get_logger(__name__) + + +def normalize_text(text: str) -> str: + """ + Normalize text by lowercasing, stripping, and removing accents. + + Args: + text (str): Input text. + + Returns: + str: Normalized text. + """ + if not text: + return "" + + # Lowercase and strip + text = text.strip().lower() + + # Remove accents + text = unicodedata.normalize("NFD", text) + text = "".join(ch for ch in text if unicodedata.category(ch) != "Mn") + + return text + + +def get_alias_set(entity: dict[str, Any], normalize: bool = True) -> set[str]: + """ + Returns the set of normalized aliases for an entity, ensuring that the + canonical_text is included as an alias. + + Args: + entity (dict[str, Any]): A dictionary containing entity information with + optional 'aliases' (list of strings) and 'canonical_text' (string) keys. + normalize (bool, optional): If True, normalizes the alias texts using + normalize_text function. Defaults to True. + Returns: + set[str]: A set of alias strings (normalized if normalize=True) including + both explicit aliases and the canonical text. + """ + aliases = set() + + # Get aliases from the entity + for alias in entity.get("aliases", []): + text = normalize_text(alias) if normalize else alias + if text: + aliases.add(text) + + # Get canonical text from the entity + canonical = ( + normalize_text(entity.get("canonical_text", "")) + if normalize + else entity.get("canonical_text", "") + ) + if canonical: + aliases.add(canonical) + + return aliases + + +def alias_jaccard(aliases_gold: set[str], aliases_pred: set[str]) -> float: + """ + Jaccard similarity between two sets of aliases. + + Args: + aliases_gold (set[str]): Set of gold aliases. + aliases_pred (set[str]): Set of predicted aliases. + + Returns: + float: Jaccard similarity score. + """ + # If both sets are empty, define similarity as 1.0 + if not aliases_gold and not aliases_pred: + return 1.0 + + # Calculate Jaccard similarity + union = aliases_gold | aliases_pred + inter = aliases_gold & aliases_pred + + return len(inter) / len(union) + + +def greedy_matching( + sim_matrix: list[list[float]], sim_threshold: float +) -> list[tuple[int, int, float]]: + """ + Perform greedy maximum matching based on similarity scores. + + Args: + sim_matrix (list[list[float]]): Similarity matrix where sim_matrix[i][j] + represents the similarity between the gold entity i and predicted + entity j. + sim_threshold (float): Minimum similarity required to consider a match + between two entities. + + Returns: + list[tuple[int, int, float]]: Tuples containing the matched gold index, + predicted index, and their similarity score. + """ + matches: list[tuple[int, int, float]] = [] + num_gold = len(sim_matrix) + num_pred = len(sim_matrix[0]) if num_gold > 0 else 0 + + # Build the list of candidate matches above the threshold + candidates: list[tuple[float, int, int]] = [] + for i in range(num_gold): + for j in range(num_pred): + sim = sim_matrix[i][j] + if sim >= sim_threshold: + candidates.append((sim, i, j)) + + # Sort descending by similarity so higher matches are selected first + candidates.sort(reverse=True, key=lambda x: x[0]) + + used_gold = set() + used_pred = set() + + for sim, i, j in candidates: + if i in used_gold or j in used_pred: + continue + used_gold.add(i) + used_pred.add(j) + matches.append((i, j, sim)) + + return matches + + +def compute_metrics_components( + gold_entities: list[dict[str, Any]], + pred_entities: list[dict[str, Any]], + sim_threshold: float = 0.3, + normalize: bool = True, +) -> dict[str, float]: + """ + Compute each component of the disambiguation metric. + + The metric includes entity-level F1, macro average alias F1, and accuracy + for labels and roles on matched entities. + + Args: + gold_entities (list[dict[str, Any]]): Ground-truth entities. + pred_entities (list[dict[str, Any]]): Predicted entities. + sim_threshold (float): Minimum alias similarity to consider entities a match. + Defaults to 0.3. + normalize (bool): If True normalizes aliases before comparison. + Defaults to True. + + Returns: + dict[str, float]: Mapping with the keys F1_ent, AliasF1_macro, Acc_label, and Acc_role. + """ + + # Handle the trivial case where both sets are empty + if not gold_entities and not pred_entities: + return { + "F1_ent": 1.0, + "AliasF1_macro": 1.0, + "Acc_label": 1.0, + "Acc_role": 1.0, + } + + # Preprocess aliases, labels and roles + gold_aliases = [get_alias_set(e, normalize=normalize) for e in gold_entities] + pred_aliases = [get_alias_set(e, normalize=normalize) for e in pred_entities] + + gold_labels = [e.get("aymurai_label") for e in gold_entities] + pred_labels = [e.get("aymurai_label") for e in pred_entities] + + gold_roles = [e.get("attributes", {}).get("role") for e in gold_entities] + pred_roles = [e.get("attributes", {}).get("role") for e in pred_entities] + + num_gold = len(gold_entities) + num_pred = len(pred_entities) + + # Build the alias similarity matrix + sim_matrix = [] + for i in range(num_gold): + row = [] + for j in range(num_pred): + sim = alias_jaccard(gold_aliases[i], pred_aliases[j]) + row.append(sim) + sim_matrix.append(row) + + # Perform greedy matching + matches = greedy_matching(sim_matrix, sim_threshold=sim_threshold) + + TP = len(matches) + FN = num_gold - TP + FP = num_pred - TP + + # Entity-level precision/recall/F1 + if TP + FP > 0: + P_ent = TP / (TP + FP) + else: + P_ent = 0.0 + + if TP + FN > 0: + R_ent = TP / (TP + FN) + else: + R_ent = 0.0 + + if P_ent + R_ent > 0: + F1_ent = 2 * P_ent * R_ent / (P_ent + R_ent) + else: + F1_ent = 0.0 + + # Alias-level F1 macro + if TP > 0: + alias_f1_sum = 0.0 + label_correct = 0 + role_correct = 0 + + for i, j, _sim in matches: + A_g = gold_aliases[i] + A_p = pred_aliases[j] + + inter = len(A_g & A_p) + P_alias = inter / len(A_p) if len(A_p) > 0 else 0.0 + R_alias = inter / len(A_g) if len(A_g) > 0 else 0.0 + if P_alias + R_alias > 0: + F1_alias = 2 * P_alias * R_alias / (P_alias + R_alias) + else: + F1_alias = 0.0 + + alias_f1_sum += F1_alias + + # Label accuracy on matched entities + if gold_labels[i] == pred_labels[j]: + label_correct += 1 + + # Role accuracy on matched entities + if gold_roles[i] == pred_roles[j]: + role_correct += 1 + + AliasF1_macro = alias_f1_sum / TP + Acc_label = label_correct / TP + Acc_role = role_correct / TP + else: + AliasF1_macro = 0.0 + Acc_label = 0.0 + Acc_role = 0.0 + + return { + "F1_ent": F1_ent, + "AliasF1_macro": AliasF1_macro, + "Acc_label": Acc_label, + "Acc_role": Acc_role, + } + + +def evaluate_disambiguation( + gold_json: Any, + pred_json: Any, + w_ent: float = 0.4, + w_alias: float = 0.35, + w_label: float = 0.2, + w_role: float = 0.05, + sim_threshold: float = 0.3, + normalize: bool = True, +) -> tuple[float, dict[str, float]]: + """ + Evaluate disambiguation predictions against ground truth entities. + + Args: + gold_json (Any): Ground-truth entities as a parsed list or JSON string. + pred_json (Any): Predicted entities as a parsed list or JSON string. + w_ent (float): Weight assigned to entity-level F1. Defaults to 0.4. + w_alias (float): Weight assigned to alias macro F1. Defaults to 0.35. + w_label (float): Weight assigned to label accuracy. Defaults to 0.2. + w_role (float): Weight assigned to role accuracy. Defaults to 0.05. + sim_threshold (float): Minimum alias similarity to consider entities a match. + Defaults to 0.3. + normalize (bool): If True normalizes aliases before comparison. + Defaults to True. + + Returns: + tuple[float, dict[str, float]]: Overall disambiguation score and detailed metrics components. + """ + + # Parse JSON strings if necessary + if isinstance(gold_json, str): + gold_entities = json.loads(gold_json) + else: + gold_entities = gold_json + + if isinstance(pred_json, str): + pred_entities = json.loads(pred_json) + else: + pred_entities = pred_json + + metrics = compute_metrics_components( + gold_entities, pred_entities, sim_threshold=sim_threshold, normalize=normalize + ) + + score = ( + w_ent * metrics["F1_ent"] + + w_alias * metrics["AliasF1_macro"] + + w_label * metrics["Acc_label"] + + w_role * metrics["Acc_role"] + ) + + return score, metrics + + +def evaluate_prediction_directories( + test_dir: Path, + preds_dir: Path, + *, + sim_threshold: float = 0.3, + normalize: bool = True, +) -> tuple[list[tuple[str, float, dict[str, float]]], float]: + """ + Evaluate predictions stored on disk against the gold standard set. + + Args: + test_dir (Path): Directory containing gold json files. + preds_dir (Path): Directory containing predicted json files. + sim_threshold (float): Minimum alias similarity to consider entities a match. + Defaults to 0.3. + normalize (bool): If True normalizes aliases before comparison. + Defaults to True. + + Returns: + tuple[list[tuple[str, float, dict[str, float]]], float]: Detailed results + per document and the average score across all evaluated documents. + """ + test_dir = Path(test_dir) + preds_dir = Path(preds_dir) + + results = [] + + for test_path in sorted(test_dir.glob("*.json")): + prefix = test_path.name[: -len(".json")] + pred_path = preds_dir / f"{prefix}.json" + + if not pred_path.exists(): + logger.warning(f"Prediction for {test_path.name} was not found; skipping.") + continue + + gold_data = load_json(test_path) + pred_data = load_json(pred_path) + + score, metrics = evaluate_disambiguation( + gold_data, + pred_data, + sim_threshold=sim_threshold, + normalize=normalize, + ) + results.append((prefix, score, metrics)) + + average_score = ( + sum(score for _, score, _ in results) / len(results) + if results + else float("nan") + ) + + return results, average_score diff --git a/aymurai/meta/entities.py b/aymurai/meta/entities.py index 64170307..08eca7ae 100644 --- a/aymurai/meta/entities.py +++ b/aymurai/meta/entities.py @@ -130,3 +130,9 @@ def validate_entity_id(self) -> CanonicalEntity: object.__setattr__(self, "entity_id", uuid5(NAMESPACE_URL, seed)) return self + + +class CanonicalEntities(BaseModel): + """Collection of canonical entities.""" + + canonical_entities: list[CanonicalEntity] diff --git a/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb index 99477159..65318651 100644 --- a/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb +++ b/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb @@ -30,39 +30,12 @@ "from pathlib import Path\n", "from typing import Iterable\n", "\n", - "import ollama\n", "import requests\n", - "from IPython.display import Markdown\n", - "from ollama import ChatResponse\n", - "from pydantic import BaseModel\n", - "from pydantic.json_schema import JsonSchemaValue\n", - "from rich.pretty import pprint\n", "from tqdm import tqdm\n", "\n", - "from aymurai.meta.entities import CanonicalEntity # , EntityRelation\n", - "from aymurai.utils.json_data import get_pretty, load_json, save_json" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "46b328aa", - "metadata": {}, - "outputs": [], - "source": [ - "os.environ[\"OLLAMA_KEEP_ALIVE\"] = \"0\"\n", - "print(\"OLLAMA_KEEP_ALIVE set to 0. Models will unload immediately after use.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "758353c4", - "metadata": {}, - "outputs": [], - "source": [ - "DATA_DIR = \"/resources/data/restricted/summarization\"\n", - "API_URL = \"http://localhost:8899\" # Url for debugger. change it to your own" + "from aymurai.llm_providers import OllamaLLMProvider\n", + "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", + "from aymurai.utils.json_data import get_pretty, save_json" ] }, { @@ -80,10 +53,10 @@ "metadata": {}, "outputs": [], "source": [ - "BASE_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", - "ENDPOINT = f\"{BASE_URL}/misc/document-extract\"\n", + "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", + "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", "DATA_ROOT = Path(\n", - " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", + " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/disambiguation-eval\")\n", ")\n", "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", @@ -253,47 +226,6 @@ "## CanonicalEntity extraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0e2a8113", - "metadata": {}, - "outputs": [], - "source": [ - "# Function to get chat response from the model\n", - "def get_chat_response(\n", - " user_prompt: str,\n", - " model: str = \"gpt-oss:20b\",\n", - " system_prompt: str = \"Sos un asistente jurídico que resume sin inventar nada.\",\n", - " options: dict = {\"temperature\": 0},\n", - " format: JsonSchemaValue | None = None,\n", - ") -> ChatResponse:\n", - " \"\"\"\n", - " Get chat response from the model.\n", - "\n", - " Args:\n", - " user_prompt (str): The prompt from the user.\n", - " model (str, optional): The model to use. Defaults to \"gpt-oss:20b\".\n", - " system_prompt (str, optional): The system prompt. Defaults to \"Sos un asistente jurídico que resume sin inventar nada.\".\n", - " options (dict, optional): The options for the chat. Defaults to {\"temperature\": 0}.\n", - " format (JsonSchemaValue | None, optional): The format for the response. Defaults to None.\n", - "\n", - " Returns:\n", - " ChatResponse: The response from the chat.\n", - " \"\"\"\n", - " response = ollama.chat(\n", - " model=model,\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options=options,\n", - " format=format,\n", - " )\n", - "\n", - " return response" - ] - }, { "cell_type": "code", "execution_count": null, @@ -301,19 +233,24 @@ "metadata": {}, "outputs": [], "source": [ - "# Sanity check\n", - "get_chat_response(\"hola, ¿cómo estás?\")" + "# Available models\n", + "# model = \"gpt-oss:20b\"\n", + "# model = \"llama3\"\n", + "# model = \"phi4:14b\"\n", + "# model = \"llama3.1:8b\"\n", + "model = \"gemma3:270m\"" ] }, { "cell_type": "code", "execution_count": null, - "id": "09631e51", + "id": "25284997", "metadata": {}, "outputs": [], "source": [ - "class CanonicalEntities(BaseModel):\n", - " canonical_entities: list[CanonicalEntity]" + "# Sanity check\n", + "provider = OllamaLLMProvider(model=model)\n", + "provider.generate(\"hola, ¿cómo estás?\")" ] }, { @@ -465,17 +402,20 @@ " ner_output_json=ner_output_json,\n", " )\n", "\n", - " # Get chat response\n", - " response = get_chat_response(\n", - " user_prompt=user_prompt,\n", - " model=model,\n", - " system_prompt=system_prompt,\n", + " # Get canonical entities from the model\n", + " provider = OllamaLLMProvider(model=model)\n", + " response = provider.generate(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options={\"temperature\": 0},\n", " format=CanonicalEntities.model_json_schema(),\n", " )\n", "\n", " # Parse canonical entities from response\n", " canonical_entities = [\n", - " output for output in json.loads(response.message.content)[\"canonical_entities\"]\n", + " output for output in json.loads(response.text)[\"canonical_entities\"]\n", " ]\n", "\n", " canonical_entities = [\n", @@ -493,24 +433,38 @@ "metadata": {}, "outputs": [], "source": [ + "output_root = Path(DATA_ROOT) / \"preds\"\n", + "output_root.mkdir(parents=True, exist_ok=True)\n", + "\n", + "prompt_version = \"1\"\n", + "\n", + "timestamp = time.strftime(\"%y%m%d_%H%M\")\n", + "base_dir_name = f\"preds-{model}-pv{prompt_version}-{timestamp}\"\n", + "output_dir = output_root / base_dir_name\n", + "\n", + "output_dir.mkdir(parents=True, exist_ok=False)\n", + "print(f\"Saving canonical entities to {output_dir}\")\n", + "\n", "for doc_path in documents:\n", " print(f\"Processing document: {doc_path}\")\n", "\n", " try:\n", - " canonical_entities = extract_canonical_entities(doc_path)\n", + " canonical_entities = extract_canonical_entities(doc_path, model=model)\n", " print(f\"Extracted {len(canonical_entities)} canonical entities.\")\n", "\n", " target_filename = re.sub(\n", - " r\"\\s+\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", " )\n", " target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)\n", + " target_path = output_dir / f\"{target_filename}.json\"\n", + "\n", " save_json(\n", " [\n", " canonical_entity.model_dump()\n", " | {\"entity_id\": canonical_entity.entity_id.hex}\n", " for canonical_entity in canonical_entities\n", " ],\n", - " f\"entities-to-review/{target_filename}-canonical-entities.json\",\n", + " str(target_path),\n", " )\n", "\n", " except Exception as e:\n", diff --git a/notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb b/notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb new file mode 100644 index 00000000..ebe84474 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb @@ -0,0 +1,431 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "10301fb9", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import unicodedata\n", + "from pathlib import Path\n", + "from typing import Any, Dict, Iterable, List, Optional, Set, Tuple\n", + "\n", + "from IPython.display import Markdown\n", + "from ollama import ChatResponse\n", + "from pydantic import BaseModel\n", + "from pydantic.json_schema import JsonSchemaValue\n", + "from rich.pretty import pprint\n", + "from tqdm import tqdm\n", + "\n", + "from aymurai.meta.entities import CanonicalEntity # , EntityRelation\n", + "from aymurai.utils.json_data import get_pretty, load_json, save_json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93f3977b", + "metadata": {}, + "outputs": [], + "source": [ + "def normalize_text(text: str) -> str:\n", + " \"\"\"\n", + " Normaliza un alias:\n", + " - pasa a minúsculas\n", + " - elimina espacios extra\n", + " - quita tildes/acentos\n", + " \"\"\"\n", + " if text is None:\n", + " return \"\"\n", + " text = text.strip().lower()\n", + " # eliminar acentos\n", + " text = unicodedata.normalize(\"NFD\", text)\n", + " text = \"\".join(ch for ch in text if unicodedata.category(ch) != \"Mn\")\n", + " return text" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cfaeeada", + "metadata": {}, + "outputs": [], + "source": [ + "def get_alias_set(entity: Dict[str, Any]) -> Set[str]:\n", + " \"\"\"\n", + " Devuelve el conjunto de aliases normalizados para una entidad,\n", + " asegurándose de incluir el canonical_text como alias.\n", + " \"\"\"\n", + " aliases = set()\n", + " # aliases explícitos\n", + " for a in entity.get(\"aliases\", []):\n", + " norm = normalize_text(a)\n", + " if norm:\n", + " aliases.add(norm)\n", + " # incluir canonical_text\n", + " canonical = normalize_text(entity.get(\"canonical_text\", \"\"))\n", + " if canonical:\n", + " aliases.add(canonical)\n", + " return aliases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "789f9be0", + "metadata": {}, + "outputs": [], + "source": [ + "def alias_jaccard(aliases_gold: Set[str], aliases_pred: Set[str]) -> float:\n", + " \"\"\"\n", + " Similaridad Jaccard entre conjuntos de aliases.\n", + " \"\"\"\n", + " if not aliases_gold and not aliases_pred:\n", + " return 1.0\n", + " union = aliases_gold | aliases_pred\n", + " if not union:\n", + " return 0.0\n", + " inter = aliases_gold & aliases_pred\n", + " return len(inter) / len(union)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b551edbc", + "metadata": {}, + "outputs": [], + "source": [ + "def greedy_matching(\n", + " sim_matrix: List[List[float]], sim_threshold: float\n", + ") -> List[Tuple[int, int, float]]:\n", + " \"\"\"\n", + " Emparejamiento greedy máximo por similitud:\n", + " - sim_matrix[i][j] = similitud entre entidad gold i y pred j\n", + " - sim_threshold: mínimo para considerar un match\n", + "\n", + " Devuelve una lista de tuplas (i_gold, j_pred, sim).\n", + " \"\"\"\n", + " matches: List[Tuple[int, int, float]] = []\n", + " num_gold = len(sim_matrix)\n", + " num_pred = len(sim_matrix[0]) if num_gold > 0 else 0\n", + "\n", + " # Generar la lista de candidatos (i, j, sim) por encima del umbral\n", + " candidates: List[Tuple[float, int, int]] = []\n", + " for i in range(num_gold):\n", + " for j in range(num_pred):\n", + " sim = sim_matrix[i][j]\n", + " if sim >= sim_threshold:\n", + " candidates.append((sim, i, j))\n", + "\n", + " # Ordenar descendente por similitud\n", + " candidates.sort(reverse=True, key=lambda x: x[0])\n", + "\n", + " used_gold = set()\n", + " used_pred = set()\n", + "\n", + " for sim, i, j in candidates:\n", + " if i in used_gold or j in used_pred:\n", + " continue\n", + " used_gold.add(i)\n", + " used_pred.add(j)\n", + " matches.append((i, j, sim))\n", + "\n", + " return matches\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b59d4083", + "metadata": {}, + "outputs": [], + "source": [ + "def compute_metrics_components(\n", + " gold_entities: List[Dict[str, Any]],\n", + " pred_entities: List[Dict[str, Any]],\n", + " sim_threshold: float = 0.3,\n", + ") -> Dict[str, float]:\n", + " \"\"\"\n", + " Calcula los componentes de la métrica:\n", + " - F1_ent: F1 de entidades (detección de entidades canónicas)\n", + " - AliasF1_macro: F1 macro promedio sobre aliases por entidad emparejada\n", + " - Acc_label: accuracy de labels en entidades emparejadas\n", + " - Acc_role: accuracy de roles en entidades emparejadas\n", + " \"\"\"\n", + "\n", + " # Caso trivial: sin entidades en ambos\n", + " if not gold_entities and not pred_entities:\n", + " return {\n", + " \"F1_ent\": 1.0,\n", + " \"AliasF1_macro\": 1.0,\n", + " \"Acc_label\": 1.0,\n", + " \"Acc_role\": 1.0,\n", + " }\n", + "\n", + " # Preprocesar aliases, labels y roles\n", + " gold_aliases = [get_alias_set(e) for e in gold_entities]\n", + " pred_aliases = [get_alias_set(e) for e in pred_entities]\n", + "\n", + " gold_labels = [e.get(\"aymurai_label\") for e in gold_entities]\n", + " pred_labels = [e.get(\"aymurai_label\") for e in pred_entities]\n", + "\n", + " gold_roles = [e.get(\"attributes\", {}).get(\"role\") for e in gold_entities]\n", + " pred_roles = [e.get(\"attributes\", {}).get(\"role\") for e in pred_entities]\n", + "\n", + " num_gold = len(gold_entities)\n", + " num_pred = len(pred_entities)\n", + "\n", + " # Matriz de similitud de aliases\n", + " sim_matrix: List[List[float]] = []\n", + " for i in range(num_gold):\n", + " row = []\n", + " for j in range(num_pred):\n", + " sim = alias_jaccard(gold_aliases[i], pred_aliases[j])\n", + " row.append(sim)\n", + " sim_matrix.append(row)\n", + "\n", + " # Matching greedy\n", + " matches = greedy_matching(sim_matrix, sim_threshold=sim_threshold)\n", + "\n", + " TP = len(matches)\n", + " FN = num_gold - TP\n", + " FP = num_pred - TP\n", + "\n", + " # Entidad-level precision/recall/F1\n", + " if TP + FP > 0:\n", + " P_ent = TP / (TP + FP)\n", + " else:\n", + " P_ent = 0.0\n", + "\n", + " if TP + FN > 0:\n", + " R_ent = TP / (TP + FN)\n", + " else:\n", + " R_ent = 0.0\n", + "\n", + " if P_ent + R_ent > 0:\n", + " F1_ent = 2 * P_ent * R_ent / (P_ent + R_ent)\n", + " else:\n", + " F1_ent = 0.0\n", + "\n", + " # Alias-level F1 macro\n", + " if TP > 0:\n", + " alias_f1_sum = 0.0\n", + " label_correct = 0\n", + " role_correct = 0\n", + "\n", + " for i, j, _sim in matches:\n", + " A_g = gold_aliases[i]\n", + " A_p = pred_aliases[j]\n", + "\n", + " inter = len(A_g & A_p)\n", + " P_alias = inter / len(A_p) if len(A_p) > 0 else 0.0\n", + " R_alias = inter / len(A_g) if len(A_g) > 0 else 0.0\n", + " if P_alias + R_alias > 0:\n", + " F1_alias = 2 * P_alias * R_alias / (P_alias + R_alias)\n", + " else:\n", + " F1_alias = 0.0\n", + "\n", + " alias_f1_sum += F1_alias\n", + "\n", + " # label\n", + " if gold_labels[i] == pred_labels[j]:\n", + " label_correct += 1\n", + "\n", + " # rol\n", + " if gold_roles[i] == pred_roles[j]:\n", + " role_correct += 1\n", + "\n", + " AliasF1_macro = alias_f1_sum / TP\n", + " Acc_label = label_correct / TP\n", + " Acc_role = role_correct / TP\n", + " else:\n", + " AliasF1_macro = 0.0\n", + " Acc_label = 0.0\n", + " Acc_role = 0.0\n", + "\n", + " return {\n", + " \"F1_ent\": F1_ent,\n", + " \"AliasF1_macro\": AliasF1_macro,\n", + " \"Acc_label\": Acc_label,\n", + " \"Acc_role\": Acc_role,\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14cab697", + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate_disambiguation(\n", + " gold_json: Any,\n", + " pred_json: Any,\n", + " w_ent: float = 0.4,\n", + " w_alias: float = 0.35,\n", + " w_label: float = 0.2,\n", + " w_role: float = 0.05,\n", + " sim_threshold: float = 0.3,\n", + ") -> Tuple[float, Dict[str, float]]:\n", + " \"\"\"\n", + " Función principal pedida:\n", + " - gold_json: JSON ground truth (lista de entidades o string JSON)\n", + " - pred_json: JSON predicho (lista de entidades o string JSON)\n", + " - w_ent, w_alias, w_label, w_role: pesos de cada componente\n", + " - sim_threshold: umbral mínimo de similitud de aliases para emparejar entidades\n", + "\n", + " Devuelve una tupla (Score_global, métricas_componentes) donde:\n", + " - Score_global es un escalar entre 0 y 1.\n", + " - métricas_componentes es un dict con los valores individuales de cada métrica.\n", + " \"\"\"\n", + "\n", + " # Si vienen como string, parsear JSON\n", + " if isinstance(gold_json, str):\n", + " gold_entities = json.loads(gold_json)\n", + " else:\n", + " gold_entities = gold_json\n", + "\n", + " if isinstance(pred_json, str):\n", + " pred_entities = json.loads(pred_json)\n", + " else:\n", + " pred_entities = pred_json\n", + "\n", + " metrics = compute_metrics_components(\n", + " gold_entities,\n", + " pred_entities,\n", + " sim_threshold=sim_threshold,\n", + " )\n", + "\n", + " score = (\n", + " w_ent * metrics[\"F1_ent\"]\n", + " + w_alias * metrics[\"AliasF1_macro\"]\n", + " + w_label * metrics[\"Acc_label\"]\n", + " + w_role * metrics[\"Acc_role\"]\n", + " )\n", + "\n", + " return score, metrics\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12f75b12", + "metadata": {}, + "outputs": [], + "source": [ + "# Ejemplo de JSON gold y pred\n", + "gold = [\n", + " {\n", + " \"entity_id\": \"GT_1\",\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"Juan Pérez\",\n", + " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\"],\n", + " \"attributes\": {\"role\": \"imputado\"},\n", + " \"relations\": [],\n", + " }\n", + "]\n", + "\n", + "pred = [\n", + " {\n", + " \"entity_id\": \"P_1\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Juan Pérez\",\n", + " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\"],\n", + " \"attributes\": {\"role\": \"imputado\"},\n", + " \"relations\": [],\n", + " }\n", + "]\n", + "\n", + "\n", + "score, metrics = evaluate_disambiguation(gold, pred)\n", + "print(\"Metrics vector:\", metrics)\n", + "print(\"Score global:\", score)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d1a279b", + "metadata": {}, + "outputs": [], + "source": [ + "# Ejemplo de JSON gold y pred\n", + "gold = [\n", + " {\n", + " \"entity_id\": \"GT_1\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Juan Pérez\",\n", + " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\"],\n", + " \"attributes\": {\"role\": \"imputado\"},\n", + " \"relations\": [],\n", + " },\n", + " {\n", + " \"entity_id\": \"GT_2\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"María Gómez\",\n", + " \"aliases\": [\"María\", \"Sra. Gómez\"],\n", + " \"attributes\": {\"role\": \"imputado\"},\n", + " \"relations\": [],\n", + " },\n", + "]\n", + "\n", + "pred = [\n", + " {\n", + " \"entity_id\": \"P_1\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Juan Pérez\",\n", + " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\", \"Pérez Juan\"],\n", + " \"attributes\": {\"role\": \"imputado\"},\n", + " \"relations\": [],\n", + " },\n", + " {\n", + " \"entity_id\": \"GT_2\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"María Gómez\",\n", + " \"aliases\": [\"María\", \"Sra. Gómez\"],\n", + " \"attributes\": {\"role\": \"imputado\"},\n", + " \"relations\": [],\n", + " },\n", + "]\n", + "\n", + "score, metrics = evaluate_disambiguation(gold, pred)\n", + "print(\"Metrics vector:\", metrics)\n", + "print(\"Score global:\", score)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb0c3ddf", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb b/notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb new file mode 100644 index 00000000..05c54a17 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb @@ -0,0 +1,110 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "164a8904", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "from pathlib import Path\n", + "\n", + "from aymurai.evaluation.metrics import evaluate_prediction_directories\n", + "\n", + "project_root = Path.cwd().parents[2]\n", + "if str(project_root) not in sys.path:\n", + " sys.path.insert(0, str(project_root))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72f996d1", + "metadata": {}, + "outputs": [], + "source": [ + "# Ajusta estas rutas antes de ejecutar\n", + "TEST_DIR = Path(\"/resources/data/restricted/disambiguation-eval/test\")\n", + "PREDS_DIR = Path(\n", + " \"/resources/data/restricted/disambiguation-eval/preds/preds_phi4:14b_pv1_251125_1716\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7b456c9", + "metadata": {}, + "outputs": [], + "source": [ + "# Normalizando los textos\n", + "results, average = evaluate_prediction_directories(TEST_DIR, PREDS_DIR)\n", + "\n", + "if not results:\n", + " print(\"No se encontraron pares test/pred que evaluar.\")\n", + "else:\n", + " for doc_id, score, metrics in results:\n", + " print(f\"Documento: {doc_id}\")\n", + " print(f\" Score global: {score:.4f}\")\n", + " for metric_name, value in metrics.items():\n", + " print(f\" {metric_name}: {value:.4f}\")\n", + " print()\n", + "\n", + " print(f\"Promedio Score_global: {average:.4f}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80f08e1d", + "metadata": {}, + "outputs": [], + "source": [ + "# Sin Normalizar los textos\n", + "results, average = evaluate_prediction_directories(TEST_DIR, PREDS_DIR, normalize=False)\n", + "\n", + "if not results:\n", + " print(\"No se encontraron pares test/pred que evaluar.\")\n", + "else:\n", + " for doc_id, score, metrics in results:\n", + " print(f\"Documento: {doc_id}\")\n", + " print(f\" Score global: {score:.4f}\")\n", + " for metric_name, value in metrics.items():\n", + " print(f\" {metric_name}: {value:.4f}\")\n", + " print()\n", + "\n", + " print(f\"Promedio Score_global: {average:.4f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fd5e8b7", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md b/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md new file mode 100644 index 00000000..5f02a5f2 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md @@ -0,0 +1,500 @@ +# Métrica de evaluación para el desambiguador de entidades + +Este documento describe la métrica con la que evaluamos el módulo de **desambiguación de entidades canónicas** en documentos judiciales. La métrica es independiente del NER (que se evalúa por separado con F1 por clase) y se centra en medir la calidad de la resolución de entidades. + +La métrica: + +- recibe dos JSON: + - `gold_json`: referencia (ground truth), + - `pred_json`: salida del desambiguador; +- calcula cuatro componentes complementarios: +- $F1_{\text{ent}}$: calidad en la **detección de entidades canónicas**, +- $\text{AliasF1}_{\text{macro}}$: calidad en la **agrupación de aliases**, +- $Acc_{\text{label}}$: exactitud del **label** (`aymurai_label`), +- $Acc_{\text{rol}}$: exactitud del **rol** (`attributes["role"]`); +- devuelve un escalar final como combinación ponderada: + +$$ +\text{Score}_{\text{global}} = +w_{\text{ent}} \cdot F1_{\text{ent}} + +w_{\text{alias}} \cdot \text{AliasF1}_{\text{macro}} + +w_{\text{label}} \cdot Acc_{\text{label}} + +w_{\text{rol}} \cdot Acc_{\text{rol}}. +$$ + +Valores por defecto de los pesos: + +- $w_{\text{ent}} = 0.40$ +- $w_{\text{alias}} = 0.35$ +- $w_{\text{label}} = 0.20$ +- $w_{\text{rol}} = 0.05$ + +--- + +## 1. Modelo de datos y notación + +Tanto `gold_json` como `pred_json` se modelan como una **lista de entidades canónicas**. Cada entidad sigue la estructura: + +```json +{ + "entity_id": "", + "aymurai_label": "", + "canonical_text": "", + "aliases": [], + "attributes": {}, + "relations": [] +} +``` + +Denotamos: + +- Conjunto de entidades gold (referencia): $G = \{ g_1, g_2, \dots, g_N \}$ +- Conjunto de entidades predichas: $P = \{ p_1, p_2, \dots, p_M \}$ + +Para cada entidad $e \in G \cup P$: + +- $\text{label}(e)$ es su `aymurai_label`. +- $\text{aliases}(e)$ es el conjunto de aliases (incluyendo `canonical_text`). +- $\text{rol}(e)$ es `attributes["role"]` si existe (en caso contrario es `None`). + +La métrica está inspirada en trabajos de **entity resolution** y **coreference**, donde interesa comparar la formación de clusters más que strings exactos. + +--- + +## 2. Preprocesamiento: normalización y conjuntos de aliases + +Cada alias se normaliza para mitigar variaciones de formato. Se procesa tanto la lista `aliases` como `canonical_text`, y se construye un conjunto de strings normalizados que representa a la entidad. + +### 2.1. Normalización de texto + +Sea una función $\text{norm}(\cdot)$ que: + +1. convierte a minúsculas, +2. aplica `strip()` para remover espacios extras, +3. elimina tildes y acentos utilizando normalización Unicode. + +```python +import unicodedata + +def normalize_text(text: str) -> str: + if text is None: + return "" + text = text.strip().lower() + text = unicodedata.normalize("NFD", text) + text = "".join(ch for ch in text if unicodedata.category(ch) != "Mn") + return text +``` + +### 2.2. Construcción del conjunto de aliases + +Para cada entidad $e$: + +$$ +\text{aliases}(e) = \{ \text{norm}(a) \mid a \in e.\text{aliases} \} \cup \{ \text{norm}(e.\text{canonical\_text}) \}. +$$ + +```python +from typing import Any, Dict, Set + +def get_alias_set(entity: Dict[str, Any]) -> Set[str]: + aliases = { + normalize_text(a) + for a in entity.get("aliases", []) + if normalize_text(a) + } + canonical = normalize_text(entity.get("canonical_text", "")) + if canonical: + aliases.add(canonical) + return aliases +``` + +--- + +## 3. Similitud entre entidades: Jaccard sobre aliases + +Para medir qué tan similares son dos entidades, usamos la similitud de **Jaccard** sobre sus conjuntos de aliases: + +$$ +\text{sim}(g_i, p_j) = +\frac{\lvert \text{aliases}(g_i) \cap \text{aliases}(p_j) \rvert} + {\lvert \text{aliases}(g_i) \cup \text{aliases}(p_j) \rvert}. +$$ + +- Valor 1: conjuntos idénticos. +- Valor 0: conjuntos disjuntos. +- Valores intermedios: superposición parcial. + +```python +from typing import Set + +def alias_jaccard(aliases_gold: Set[str], aliases_pred: Set[str]) -> float: + if not aliases_gold and not aliases_pred: + return 1.0 + union = aliases_gold | aliases_pred + if not union: + return 0.0 + inter = aliases_gold & aliases_pred + return len(inter) / len(union) +``` + +Construimos una **matriz de similitud** $S \in \mathbb{R}^{N \times M}$ donde $S_{ij} = \text{sim}(g_i, p_j)$. + +--- + +## 4. Emparejamiento de entidades (matching gold–pred) + +### 4.1. Problema + +Necesitamos decidir qué entidad predicha corresponde a cada entidad gold. Esto es un matching bipartito: + +- Si el modelo divide una entidad real en dos, solo una predicha debería emparejarse con la gold; la otra contará como falso positivo. +- Si el modelo fusiona dos entidades distintas, solo una gold se emparejará; la otra será falso negativo. + +### 4.2. Umbral de similitud + +Se define un umbral $\tau$ (por defecto $\tau = 0.3$): + +- Si $S_{ij} < \tau$, se rechaza el match. +- Si $S_{ij} \ge \tau$, el par se considera candidato. + +### 4.3. Algoritmo greedy de emparejamiento + +Usamos un matching greedy ordenado por similitud (alternativa simple al algoritmo Húngaro): + +```python +from typing import List, Tuple + +def greedy_matching( + sim_matrix: List[List[float]], + sim_threshold: float +) -> List[Tuple[int, int, float]]: + matches: List[Tuple[int, int, float]] = [] + num_gold = len(sim_matrix) + num_pred = len(sim_matrix[0]) if num_gold > 0 else 0 + + candidates = [] + for i in range(num_gold): + for j in range(num_pred): + sim = sim_matrix[i][j] + if sim >= sim_threshold: + candidates.append((sim, i, j)) + + candidates.sort(reverse=True, key=lambda x: x[0]) + + used_gold, used_pred = set(), set() + for sim, i, j in candidates: + if i in used_gold or j in used_pred: + continue + used_gold.add(i) + used_pred.add(j) + matches.append((i, j, sim)) + + return matches +``` + +El resultado es una lista de triples $(i, j, \text{sim})$ con las parejas aceptadas. + +--- + +## 5. Métrica de entidades: $F1_{\text{ent}}$ + +Sean: + +- $\text{TP}$: número de pares emparejados. +- $\text{FN} = N - \text{TP}$: entidades gold sin match (perdidas). +- $\text{FP} = M - \text{TP}$: entidades pred sin match (inventadas o splits innecesarios). + +```math +P_{\text{ent}} = \frac{\text{TP}}{\text{TP} + \text{FP}}, \qquad +R_{\text{ent}} = \frac{\text{TP}}{\text{TP} + \text{FN}} +``` + +```math +F1_{\text{ent}} = +\begin{cases} +\dfrac{2 P_{\text{ent}} R_{\text{ent}}}{P_{\text{ent}} + R_{\text{ent}}}, & \text{si } P_{\text{ent}} + R_{\text{ent}} > 0 \\ +0, & \text{en otro caso} +\end{cases} +``` + +Interpretación: penaliza entidades faltantes, inventadas y los splits/merges incorrectos. + +--- + +## 6. Métrica de aliases: $\text{AliasF1}_{\text{macro}}$ + +Para cada par emparejado $(g_i, p_j)$: + +```math +A_g = \text{aliases}(g_i), \quad +A_p = \text{aliases}(p_j), \quad +I = |A_g \cap A_p| +``` + +```math +P_{\text{alias}}^{(i)} = \frac{I}{|A_p|}, \qquad +R_{\text{alias}}^{(i)} = \frac{I}{|A_g|} +``` + +```math +F1_{\text{alias}}^{(i)} = +\begin{cases} +\dfrac{2 P_{\text{alias}}^{(i)} R_{\text{alias}}^{(i)}} + {P_{\text{alias}}^{(i)} + R_{\text{alias}}^{(i)}}, & +\text{si } P_{\text{alias}}^{(i)} + R_{\text{alias}}^{(i)} > 0 \\ +0, & \text{en otro caso} +\end{cases} +``` + +Promediamos macro sobre los matches: + +```math +\text{AliasF1}_\text{macro} = +\frac{1}{\text{TP}} \sum_{(i,j) \in M_{\text{match}}} F1_{\text{alias}}^{(i)} +``` + +- Falta de aliases relevantes → baja el recall. +- Aliases incorrectos → baja la precision. +- Splits se reflejan en menor $F1_{\text{alias}}$. + +```python +alias_f1_sum = 0.0 +for i, j, _ in matches: + A_g = gold_aliases[i] + A_p = pred_aliases[j] + inter = len(A_g & A_p) + + P_alias = inter / len(A_p) if A_p else 0.0 + R_alias = inter / len(A_g) if A_g else 0.0 + if P_alias + R_alias > 0: + F1_alias = 2 * P_alias * R_alias / (P_alias + R_alias) + else: + F1_alias = 0.0 + + alias_f1_sum += F1_alias + +AliasF1_macro = alias_f1_sum / TP if TP > 0 else 0.0 +``` + +--- + +## 7. Exactitud de label y rol + +Para cada match \((g_i, p_j)\): + +```math +\text{correctLabel}^{(i)} = +\begin{cases} +1, & \text{si } \text{label}(g_i) = \text{label}(p_j) \\ +0, & \text{en otro caso} +\end{cases} +``` + +```math +\text{correctRol}^{(i)} = +\begin{cases} +1, & \text{si } \text{rol}(g_i) = \text{rol}(p_j) \\ +0, & \text{en otro caso} +\end{cases} +``` + +```math +Acc_{\text{label}} = +\frac{1}{\text{TP}} \sum_{(i,j) \in M_{\text{match}}} \text{correctLabel}^{(i)}, \quad +Acc_{\text{rol}} = +\frac{1}{\text{TP}} \sum_{(i,j) \in M_{\text{match}}} \text{correctRol}^{(i)}. +``` + +```python +label_correct = 0 +role_correct = 0 + +for i, j, _ in matches: + if gold_labels[i] == pred_labels[j]: + label_correct += 1 + if gold_roles[i] == pred_roles[j]: + role_correct += 1 + +Acc_label = label_correct / TP if TP > 0 else 0.0 +Acc_role = role_correct / TP if TP > 0 else 0.0 +``` + +Separar la calidad de la clusterización (aliases) de la del rotulado alinea la métrica con enfoques **entity-centric** en la literatura. + +--- + +## 8. Función `compute_metrics_components` + +Integra todos los pasos anteriores y devuelve un diccionario con los cuatro componentes: + +```python +from typing import Any, Dict, List + +def compute_metrics_components( + gold_entities: List[Dict[str, Any]], + pred_entities: List[Dict[str, Any]], + sim_threshold: float = 0.3, +) -> Dict[str, float]: + if not gold_entities and not pred_entities: + return { + "F1_ent": 1.0, + "AliasF1_macro": 1.0, + "Acc_label": 1.0, + "Acc_role": 1.0, + } + + gold_aliases = [get_alias_set(e) for e in gold_entities] + pred_aliases = [get_alias_set(e) for e in pred_entities] + + gold_labels = [e.get("aymurai_label") for e in gold_entities] + pred_labels = [e.get("aymurai_label") for e in pred_entities] + + gold_roles = [e.get("attributes", {}).get("rol") for e in gold_entities] + pred_roles = [e.get("attributes", {}).get("rol") for e in pred_entities] + + sim_matrix = [ + [alias_jaccard(g_gold, g_pred) for g_pred in pred_aliases] + for g_gold in gold_aliases + ] + + matches = greedy_matching(sim_matrix, sim_threshold=sim_threshold) + + TP = len(matches) + FN = len(gold_entities) - TP + FP = len(pred_entities) - TP + + P_ent = TP / (TP + FP) if TP + FP > 0 else 0.0 + R_ent = TP / (TP + FN) if TP + FN > 0 else 0.0 + F1_ent = 2 * P_ent * R_ent / (P_ent + R_ent) if P_ent + R_ent > 0 else 0.0 + + alias_f1_sum = 0.0 + label_correct = 0 + role_correct = 0 + + for i, j, _ in matches: + A_g = gold_aliases[i] + A_p = pred_aliases[j] + inter = len(A_g & A_p) + + P_alias = inter / len(A_p) if A_p else 0.0 + R_alias = inter / len(A_g) if A_g else 0.0 + if P_alias + R_alias > 0: + F1_alias = 2 * P_alias * R_alias / (P_alias + R_alias) + else: + F1_alias = 0.0 + alias_f1_sum += F1_alias + + if gold_labels[i] == pred_labels[j]: + label_correct += 1 + if gold_roles[i] == pred_roles[j]: + role_correct += 1 + + AliasF1_macro = alias_f1_sum / TP if TP > 0 else 0.0 + Acc_label = label_correct / TP if TP > 0 else 0.0 + Acc_role = role_correct / TP if TP > 0 else 0.0 + + return { + "F1_ent": F1_ent, + "AliasF1_macro": AliasF1_macro, + "Acc_label": Acc_label, + "Acc_role": Acc_role, + } +``` + +--- + +## 9. Métrica escalar final: `evaluate_disambiguation` + +Función de alto nivel que combina los componentes con los pesos: + +```python +import json +from typing import Any + +def evaluate_disambiguation( + gold_json: Any, + pred_json: Any, + w_ent: float = 0.4, + w_alias: float = 0.35, + w_label: float = 0.2, + w_role: float = 0.05, + sim_threshold: float = 0.3, +) -> Tuple[float, Dict[str, float]]: + if isinstance(gold_json, str): + gold_entities = json.loads(gold_json) + else: + gold_entities = gold_json + + if isinstance(pred_json, str): + pred_entities = json.loads(pred_json) + else: + pred_entities = pred_json + + metrics = compute_metrics_components( + gold_entities, + pred_entities, + sim_threshold=sim_threshold, + ) + + return ( + w_ent * metrics["F1_ent"] + + w_alias * metrics["AliasF1_macro"] + + w_label * metrics["Acc_label"] + + w_role * metrics["Acc_role"] + ) +``` + +### Justificación de los pesos por defecto + +- **$w_{\text{ent}} = 0.40$**: prioriza detectar todas las entidades canónicas sin inventarlas. +- **$w_{\text{alias}} = 0.35$**: agrupar aliases es crucial para preservar el anonimato. +- **$w_{\text{label}} = 0.20$**: el tipo de entidad importa, pero es secundario frente a la detección. +- **$w_{\text{rol}} = 0.05$**: el rol agrega contexto, pero tiene menor peso. + +Los pesos pueden ajustarse según las necesidades del proyecto. + +--- + +## 10. Ejemplo de uso + +```python +gold = [ + { + "entity_id": "GT_1", + "aymurai_label": "PER", + "canonical_text": "Juan Pérez", + "aliases": ["Juan Pérez", "Pérez, Juan"], + "attributes": {"role": "imputado"}, + "relations": [] + } +] + +pred = [ + { + "entity_id": "P_1", + "aymurai_label": "PER", + "canonical_text": "juan perez", + "aliases": ["juan perez", "perez juan"], + "attributes": {"role": "imputado"}, + "relations": [] + } +] + +score, components = evaluate_disambiguation(gold, pred) +print("Score global:", score) +print(components) +``` + +--- + +## 11. Referencias y relación con la literatura + +- Moosavi, N. S., & Strube, M. (2016). *Which coreference evaluation metric do you trust? A proposal for a link-based entity aware metric*. ACL. +- Menestrina, D., Whang, S. E., & Garcia-Molina, H. (2010). *Evaluating entity resolution results*. PVLDB. +- Binette, O., et al. (2024). *Comprehensive evaluation frameworks for entity resolution*. + +La métrica propuesta toma conceptos de la evaluación de coreference y entity resolution y los adapta a nuestro contexto judicial: + +- priorizando no perder entidades sensibles, +- evitando crear entidades inexistentes, +- y reforzando que los aliases de una misma persona no se mezclen con los de otra. From 3f040317ae661c0ad7384853fc068a4f57e413a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:15:20 -0300 Subject: [PATCH 017/101] Feature/summarization (#61) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat: Add Streamlit app for document summarization experiments * Add statistical analysis notebook for summarization performance evaluation( Visualized gaps in performance between CPU and CUDA models, llm alucinations) * 🎨 Quantitative and qualitative analysis of summaries: descriptive analysis by features, model comparison, gap analusis (CPU-CUDA), Garbage detection/outliers, analysis by document, visuailzations. * 🔒️ clear all outputs * 🎨 Improve Summary Analysis per document: cuda vs llama (same model), gemma vs llama (cuda), same document phi3 vs. phi4. Token per second gap. * ✨ Add YAML utility functions for loading and saving data * Merge dev into main for v1.1.12 (#57) * Update README.md * 🐛 bugfix: Fix XML special character escaping in DocAnonymizer * ➕ build(deps): Add python-docx package * ✨ feat: Add watermark and hyperlink functionality to document anonymization * ✨ feat: Install Archivo font in Dockerfile * 🎨 refactor: Improve Dockerfile structure and comments for clarity * ⏪ revert: Remove Archivo font installation from Dockerfile * 🔖 feat: Update aymurai package version to 1.1.11 in uv.lock * 🐛 Improve get_extension logic to fix document extraction issues on Windows and remove python-magic dependency * 🔧 Update Dockerfile to use 'bullseye' variant for Python images for improved compatibility * 🔧 Update Makefile targets for improved Docker workflow * 🔖 feat: Bump aymurai package version to 1.1.12 * ♻️ Harden get_extension with header scans and zip safeguards * 🔧 Extend document extraction timeout to 30s * 🔧 Refactor Docker workflow to build and push images using docker/build-push-action * 🔧 Fix workflow step order to correctly extract tag name before building Docker images * 🔧 Remove tag extraction step and use github.ref_name directly for Docker image builds * ⏪ Revert Docker workflow to extract tag name and use it for image versioning * Update .github/workflows/build-docker-image.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * ✏️ Remove incomplete comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: jed Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * ✨ Add GPU-enabled Ollama service to compose stack * 🔧 Add Make targets for managing Ollama service and models * 🔧 Add launch configuration and task for starting Ollama service * 🔧 Add system prompts for document summarization * 📝 Add summarization benchmark notebook * 🚚 Move statistical analysis notebook to summarization folder * ✨ Implement LLM providers module with Ollama adapter and shared abstractions * ✅ Add unit tests for LLM providers including DummyProvider and OllamaLLMProvider * 📝 Document Ollama provider usage via notebook demo * 🐛 Fix tokenizer encoding by removing unnecessary special tokens flag * ♻️ Refactor chunk handling in LLMProvider to use _append_chunk method for consistency and improved readability * ✨ Enhance Ollama provider docs and DRY response building for sync/async calls * ♻️ Refactor OllamaLLMProvider to reuse AsyncClient instance for improved efficiency * 📝 Add async examples to OllamaLLMProvider notebook * ✅ Add async coverage for OllamaLLMProvider and tighten chunking tests * ➕ Add tiktoken dependency to pyproject.toml and update version in uv.lock * 🔧 Enhance summarization prompts with additional information extraction and entity identification details * ✨ Add LLM summarization router * 📝 Add notebook for the summarization endpoint * ✏️ Fix formatting of keys in summarization defaults for consistency * ➕ Add dspy dependency and update related packages in project configuration * 🚧 WIP: Add prompt optimization notebook for summarization experiments --------- Co-authored-by: Sofi Co-authored-by: jed Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- aymurai/api/core.py | 8 + aymurai/api/endpoints/routers/llm/__init__.py | 1 + .../endpoints/routers/llm/summarization.py | 580 ++++++ aymurai/utils/yaml_data.py | 28 + .../01-summarization-benchmark.ipynb | 735 ++++++++ .../02-statistical-analysis.ipynb | 1651 +++++++++++++++++ .../03-summarization-endpoint.ipynb | 170 ++ .../04-dspy-prompt-optimization.ipynb | 955 ++++++++++ pyproject.toml | 2 + resources/llm/summarization.yml | 71 + summarization_app/app.py | 759 ++++++++ summarization_app/run_streamlit_app.sh | 10 + uv.lock | 208 ++- 13 files changed, 5177 insertions(+), 1 deletion(-) create mode 100644 aymurai/api/endpoints/routers/llm/__init__.py create mode 100644 aymurai/api/endpoints/routers/llm/summarization.py create mode 100644 aymurai/utils/yaml_data.py create mode 100644 notebooks/experiments/summarization/01-summarization-benchmark.ipynb create mode 100644 notebooks/experiments/summarization/02-statistical-analysis.ipynb create mode 100644 notebooks/experiments/summarization/03-summarization-endpoint.ipynb create mode 100644 notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb create mode 100755 resources/llm/summarization.yml create mode 100755 summarization_app/app.py create mode 100755 summarization_app/run_streamlit_app.sh diff --git a/aymurai/api/core.py b/aymurai/api/core.py index c9223455..fe955b4b 100644 --- a/aymurai/api/core.py +++ b/aymurai/api/core.py @@ -3,6 +3,7 @@ from .endpoints.routers.anonymizer import anonymizer from .endpoints.routers.anonymizer import database as anonymizer_database from .endpoints.routers.datapublic import datapublic +from .endpoints.routers.llm import summarization from .endpoints.routers.misc import convert, document_extract from .endpoints.routers.server import stats @@ -36,6 +37,13 @@ tags=["datapublic/model"], ) +# LLM +router.include_router( + summarization.router, + prefix="/llm", + tags=["llm/summarization"], +) + # Misc router.include_router(document_extract.router, tags=["document"], deprecated=True) diff --git a/aymurai/api/endpoints/routers/llm/__init__.py b/aymurai/api/endpoints/routers/llm/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/aymurai/api/endpoints/routers/llm/__init__.py @@ -0,0 +1 @@ + diff --git a/aymurai/api/endpoints/routers/llm/summarization.py b/aymurai/api/endpoints/routers/llm/summarization.py new file mode 100644 index 00000000..6e40bc02 --- /dev/null +++ b/aymurai/api/endpoints/routers/llm/summarization.py @@ -0,0 +1,580 @@ +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any, AsyncIterator + +import tiktoken +from fastapi import APIRouter, HTTPException +from fastapi.responses import StreamingResponse +from pydantic import BaseModel, ConfigDict, Field + +from aymurai.llm_providers import OllamaLLMProvider +from aymurai.logger import get_logger +from aymurai.settings import settings +from aymurai.utils.yaml_data import load_yaml + +logger = get_logger(__name__) + +router = APIRouter() + +DEFAULT_MODEL = "gpt-oss:20b" +DEFAULT_OPTIONS = {"num_ctx": 16_384, "num_predict": 4096} +DEFAULT_TOKENIZER = "o200k_harmony" +PROMPT_TEMPLATE_PATH = Path(settings.RESOURCES_BASEPATH) / "llm" / "summarization.yml" + + +def _load_prompt_defaults(path: Path) -> tuple[str, str]: + """ + Load default placeholders from the YAML template, + ensuring both required fields are present. + + Args: + path: Path to the summarization YAML file. + + Returns: + tuple[str, str]: Defaults for `information_to_extract` and `entities_to_identify`. + + Raises: + FileNotFoundError: If the YAML file cannot be located. + RuntimeError: If the defaults block is missing or incomplete. + """ + content = load_yaml(str(path)) + defaults = content.get("defaults", {}) + info = (defaults.get("information_to_extract") or "").strip() + entities = (defaults.get("entities_to_identify") or "").strip() + if not info or not entities: + raise RuntimeError("summarization defaults missing in YAML") + return info, entities + + +DEFAULT_INFORMATION_TO_EXTRACT, DEFAULT_ENTITIES_TO_IDENTIFY = _load_prompt_defaults( + PROMPT_TEMPLATE_PATH +) + + +class SummarizationRequest(BaseModel): + model_config = ConfigDict( + json_schema_extra={ + "example": { + "text": "Texto de entrada a resumir.", + "model": DEFAULT_MODEL, + "tokenizer": DEFAULT_TOKENIZER, + "options": DEFAULT_OPTIONS, + } + } + ) + + text: str = Field(..., description="Raw document to summarize.") + model: str = Field( + default=DEFAULT_MODEL, + description="Model name for Ollama. Defaults to gpt-oss:20b.", + ) + tokenizer: str | None = Field( + default=DEFAULT_TOKENIZER, + description="Tokenizer name (tiktoken). Falls back to whitespace if unavailable.", + ) + system_prompt: str | None = Field( + default=None, + description="Optional system prompt override. Defaults to the template in /resources/llm/summarization.yml.", + ) + information_to_extract: str | None = Field( + default=DEFAULT_INFORMATION_TO_EXTRACT, + description="Template variable information_to_extract.", + ) + entities_to_identify: str | None = Field( + default=DEFAULT_ENTITIES_TO_IDENTIFY, + description="Template variable entities_to_identify.", + ) + options: dict[str, Any] = Field( + default_factory=lambda: DEFAULT_OPTIONS.copy(), + description="Ollama chat options. Defaults to num_ctx=16_384 and num_predict=4096.", + ) + + +class SummarizationStep(BaseModel): + chunk_index: int + input_tokens: int + source: str + + +class SummarizationResponse(BaseModel): + summary: str + model: str + system_prompt: str + options: dict[str, Any] + chunks_used: int + steps: list[SummarizationStep] + + +def _load_template_prompt( + *, + information_to_extract: str | None, + entities_to_identify: str | None, +) -> str: + """ + Load and format the summarization prompt template. + + Args: + information_to_extract: Template placeholder describing what to extract. + entities_to_identify: Template placeholder listing entities to detect. + + Returns: + str: Formatted system prompt for summarization. + + Raises: + HTTPException: If the template file is missing or cannot be parsed. + """ + path = PROMPT_TEMPLATE_PATH + try: + content = load_yaml(str(path)) + template = content["system-prompts"]["template"] + + except FileNotFoundError as exc: + logger.exception(f"Summarization prompt template not found at {path}") + raise HTTPException( + status_code=500, + detail="Summarization prompt template not found.", + ) from exc + except Exception as exc: + logger.exception("Error loading summarization prompt template.") + raise HTTPException( + status_code=500, + detail="Unable to load summarization prompt template.", + ) from exc + + info = information_to_extract or DEFAULT_INFORMATION_TO_EXTRACT + entities = entities_to_identify or DEFAULT_ENTITIES_TO_IDENTIFY + + return template.format( + information_to_extract=info, + entities_to_identify=entities, + ) + + +def _load_tokenizer(tokenizer_name: str | None) -> tiktoken.Encoding | None: + """ + Load a tiktoken tokenizer by name. + + Args: + tokenizer_name (str | None): Name of the tokenizer to load. + + Returns: + tiktoken.Encoding | None: The loaded tokenizer encoding or None if unavailable. + """ + if not tokenizer_name: + return None + + try: + return tiktoken.get_encoding(tokenizer_name) + + except Exception: + logger.warning( + f"Tokenizer {tokenizer_name} is not available. Falling back to whitespace." + ) + return None + + +def _count_tokens(text: str, tokenizer: Any) -> int: + """ + Count the number of tokens in the given text using the provided tokenizer. + + Args: + text (str): The input text to count tokens for. + tokenizer (Any): The tokenizer to use for counting tokens. + + Returns: + int: The number of tokens in the input text. + """ + if tokenizer is None: + return len(text.split()) + try: + return len(tokenizer.encode(text)) + except Exception: + return len(text.split()) + + +def _build_provider( + model_name: str, + *, + tokenizer_name: str | None, + system_prompt: str, + context_limit: int, +) -> OllamaLLMProvider: + """ + Build an Ollama provider, adjusting available context for the system prompt. + + Args: + model_name: Name of the model to use. + tokenizer_name: Tokenizer name to attempt loading. + system_prompt: System prompt to set and budget for. + context_limit: Maximum number of context tokens. + + Returns: + OllamaLLMProvider: Configured OllamaLLMProvider instance. + """ + encoding = _load_tokenizer(tokenizer_name) + available_context = max(context_limit - _count_tokens(system_prompt, encoding), 1) + + return OllamaLLMProvider( + model=model_name, + tokenizer=encoding, + system_prompt=system_prompt, + max_context_tokens=available_context, + ) + + +async def _summarize_once( + provider: OllamaLLMProvider, + *, + text: str, + options: dict[str, Any], +) -> str: + """ + Run a single summarization call on the provider. + + Args: + provider: Initialized OllamaLLMProvider instance. + text: Chunk or combined text to summarize. + options: Provider options forwarded to the model. + + Returns: + str: The model's text output for the given input. + """ + response = await provider.async_generate(prompt=text, options=options) + return response.text + + +async def _rolling_summarize( + provider: OllamaLLMProvider, + *, + text: str, + options: dict[str, Any], +) -> tuple[str, list[SummarizationStep], int]: + """ + Summarize potentially long text by chunking and rolling summaries forward. + + Args: + provider: Initialized OllamaLLMProvider instance. + text: Full document text to summarize. + options: Provider options forwarded to the model. + + Returns: + tuple[str, list[SummarizationStep], int]: A tuple of (final_summary, steps_metadata, chunk_count). + """ + chunks = provider.chunk_text(text, max_tokens=provider.max_context_tokens) + if not chunks: + return "", [], 0 + + steps = [] + summary = await _summarize_once(provider, text=chunks[0].text, options=options) + steps.append( + SummarizationStep( + chunk_index=chunks[0].index, + input_tokens=chunks[0].token_count, + source="chunk", + ) + ) + + for chunk in chunks[1:]: + combined_text = f"{summary}\n\n{chunk.text}" + combined_tokens = provider.count_tokens(combined_text) + + if ( + provider.max_context_tokens + and combined_tokens > provider.max_context_tokens + ): + chunk_summary = await _summarize_once( + provider, + text=chunk.text, + options=options, + ) + steps.append( + SummarizationStep( + chunk_index=chunk.index, + input_tokens=chunk.token_count, + source="chunk", + ) + ) + combined_text = f"{summary}\n\n{chunk_summary}" + combined_tokens = provider.count_tokens(combined_text) + + if ( + provider.max_context_tokens + and combined_tokens > provider.max_context_tokens + ): + trimmed_parts = [] + for part in provider.chunk_text( + combined_text, max_tokens=provider.max_context_tokens + ): + tentative = " ".join(trimmed_parts + [part.text]) + if ( + provider.max_context_tokens + and provider.count_tokens(tentative) + > provider.max_context_tokens + ): + break + trimmed_parts.append(part.text) + combined_text = " ".join(trimmed_parts) + combined_tokens = provider.count_tokens(combined_text) + + summary = await _summarize_once( + provider, + text=combined_text, + options=options, + ) + steps.append( + SummarizationStep( + chunk_index=chunk.index, + input_tokens=combined_tokens, + source="summary+chunk", + ) + ) + + return summary, steps, len(chunks) + + +def _build_sse_message(payload: dict[str, Any]) -> str: + """ + Format a payload as an SSE data message. + + Args: + payload: Dictionary to serialize into the SSE data field. + + Returns: + str: Serialized SSE message string. + """ + return f"data: {json.dumps(payload)}\n\n" + + +def _trim_to_context(provider: OllamaLLMProvider, text: str) -> str: + """ + Trim combined text to respect the provider's context window. + + Args: + provider: Provider with a configured max_context_tokens. + text: Concatenated summary and chunk text. + + Returns: + str: Text trimmed to fit within the context limit. + """ + if not provider.max_context_tokens: + return text + + trimmed_parts: list[str] = [] + for part in provider.chunk_text(text, max_tokens=provider.max_context_tokens): + tentative = " ".join(trimmed_parts + [part.text]) + if ( + provider.max_context_tokens + and provider.count_tokens(tentative) > provider.max_context_tokens + ): + break + trimmed_parts.append(part.text) + + return " ".join(trimmed_parts) + + +@router.post("/summarize", response_model=SummarizationResponse) +async def summarize_document(payload: SummarizationRequest) -> SummarizationResponse: + """ + Summarize a document using a local Ollama model. + + The endpoint chunks inputs above the context limit and performs a rolling + summarization that keeps the interim summary in context for subsequent chunks. + """ + if not payload.text.strip(): + raise HTTPException(status_code=400, detail="Input text cannot be empty.") + + system_prompt = payload.system_prompt or _load_template_prompt( + information_to_extract=payload.information_to_extract, + entities_to_identify=payload.entities_to_identify, + ) + model_name = payload.model or DEFAULT_MODEL + options = {**DEFAULT_OPTIONS, **(payload.options or {})} + + try: + context_limit = int(options.get("num_ctx", DEFAULT_OPTIONS["num_ctx"])) + + except (TypeError, ValueError) as exc: + raise HTTPException( + status_code=400, + detail="num_ctx option must be an integer.", + ) from exc + + if context_limit <= 0: + raise HTTPException(status_code=400, detail="num_ctx must be greater than 0.") + + provider = _build_provider( + model_name, + tokenizer_name=payload.tokenizer, + system_prompt=system_prompt, + context_limit=context_limit, + ) + + summary, steps, chunk_count = await _rolling_summarize( + provider, + text=payload.text, + options=options, + ) + + return SummarizationResponse( + summary=summary, + model=provider.model_name, + system_prompt=system_prompt, + options=options, + chunks_used=chunk_count or 1, + steps=steps, + ) + + +@router.post("/summarize/stream") +async def stream_summarize_document( + payload: SummarizationRequest, +) -> StreamingResponse: + """ + Stream a document summary using server-sent events (text/event-stream). + + Emits: + - data: {"type": "meta", ...} + - data: {"type": "token", "chunk_index": int, "source": str, "text": str} + - data: {"type": "summary", "summary": str, "chunks_used": int, "steps": [...], "model": str} + """ + if not payload.text.strip(): + raise HTTPException(status_code=400, detail="Input text cannot be empty.") + + system_prompt = payload.system_prompt or _load_template_prompt( + information_to_extract=payload.information_to_extract, + entities_to_identify=payload.entities_to_identify, + ) + model_name = payload.model or DEFAULT_MODEL + options = {**DEFAULT_OPTIONS, **(payload.options or {})} + + try: + context_limit = int(options.get("num_ctx", DEFAULT_OPTIONS["num_ctx"])) + + except (TypeError, ValueError) as exc: + raise HTTPException( + status_code=400, + detail="num_ctx option must be an integer.", + ) from exc + + if context_limit <= 0: + raise HTTPException(status_code=400, detail="num_ctx must be greater than 0.") + + provider = _build_provider( + model_name, + tokenizer_name=payload.tokenizer, + system_prompt=system_prompt, + context_limit=context_limit, + ) + + async def event_stream() -> AsyncIterator[str]: + yield _build_sse_message( + { + "type": "meta", + "model": provider.model_name, + "system_prompt": system_prompt, + "options": options, + } + ) + + chunks = provider.chunk_text( + payload.text, + max_tokens=provider.max_context_tokens, + ) + + if not chunks: + yield _build_sse_message( + { + "type": "summary", + "summary": "", + "chunks_used": 0, + "steps": [], + "model": provider.model_name, + } + ) + return + + steps: list[dict[str, Any]] = [] + summary = "" + + for chunk in chunks: + source = "chunk" if not summary else "summary+chunk" + combined_text = f"{summary}\n\n{chunk.text}" if summary else chunk.text + combined_tokens = provider.count_tokens(combined_text) + + if ( + provider.max_context_tokens + and combined_tokens > provider.max_context_tokens + and summary + ): + # Summarize the current chunk alone when the combined context would overflow. + chunk_summary = "" + async for resp in provider.async_stream( + prompt=chunk.text, + options=options, + ): + token = resp.text + if not token: + continue + chunk_summary += token + yield _build_sse_message( + { + "type": "token", + "chunk_index": chunk.index, + "source": "chunk", + "text": token, + } + ) + + steps.append( + { + "chunk_index": chunk.index, + "input_tokens": chunk.token_count, + "source": "chunk", + } + ) + + combined_text = f"{summary}\n\n{chunk_summary}" + combined_text = _trim_to_context(provider, combined_text) + source = "summary+chunk" + combined_tokens = provider.count_tokens(combined_text) + + updated_summary = "" + async for resp in provider.async_stream( + prompt=combined_text, + options=options, + ): + token = resp.text + if not token: + continue + updated_summary += token + yield _build_sse_message( + { + "type": "token", + "chunk_index": chunk.index, + "source": source, + "text": token, + } + ) + + steps.append( + { + "chunk_index": chunk.index, + "input_tokens": combined_tokens, + "source": source, + } + ) + summary = updated_summary + + yield _build_sse_message( + { + "type": "summary", + "summary": summary, + "chunks_used": len(chunks), + "steps": steps, + "model": provider.model_name, + } + ) + + return StreamingResponse(event_stream(), media_type="text/event-stream") diff --git a/aymurai/utils/yaml_data.py b/aymurai/utils/yaml_data.py new file mode 100644 index 00000000..97d30fbb --- /dev/null +++ b/aymurai/utils/yaml_data.py @@ -0,0 +1,28 @@ +import yaml + + +def load_yaml(file_path: str) -> dict: + """ + Loads a yaml file and returns its content as a dictionary. + + Args: + file_path (str): The path to the yaml file. + + Returns: + dict: The yaml file content. + """ + with open(file_path, "r") as f: + content = yaml.safe_load(f) + return content + + +def save_yaml(dict_data: dict, file_path: str) -> None: + """ + Saves a dictionary as a yaml file. + + Args: + dict_data (dict): The dictionary to be saved. + file_path (str): The path to the file where the yaml will be saved. + """ + with open(file_path, "w") as f: + f.write(yaml.dump(dict_data)) diff --git a/notebooks/experiments/summarization/01-summarization-benchmark.ipynb b/notebooks/experiments/summarization/01-summarization-benchmark.ipynb new file mode 100644 index 00000000..1178ac62 --- /dev/null +++ b/notebooks/experiments/summarization/01-summarization-benchmark.ipynb @@ -0,0 +1,735 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f900ed31", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "%load_ext rich" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03359440", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import mimetypes\n", + "import os\n", + "import time\n", + "from pathlib import Path\n", + "from typing import Iterable\n", + "\n", + "import ollama\n", + "import requests\n", + "from IPython.display import Markdown\n", + "from ollama import ChatResponse\n", + "from tqdm import tqdm\n", + "\n", + "from aymurai.utils.json_data import load_json, save_json\n", + "from aymurai.utils.yaml_data import load_yaml" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6297e02c", + "metadata": {}, + "outputs": [], + "source": [ + "os.environ[\"OLLAMA_KEEP_ALIVE\"] = \"0\"\n", + "print(\"OLLAMA_KEEP_ALIVE set to 0. Models will unload immediately after use.\")" + ] + }, + { + "cell_type": "markdown", + "id": "1d59c9c9", + "metadata": {}, + "source": [ + "## Documents" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28110bf0", + "metadata": {}, + "outputs": [], + "source": [ + "BASE_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", + "ENDPOINT = f\"{BASE_URL}/misc/document-extract\"\n", + "DATA_ROOT = Path(\n", + " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3fb80b7", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "print(f\"Discovered {len(documents)} documents.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aebe1738", + "metadata": {}, + "outputs": [], + "source": [ + "def call_extraction_api(\n", + " session: requests.Session, file_path: Path\n", + ") -> dict[str, object]:\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " ENDPOINT,\n", + " files=files,\n", + " timeout=REQUEST_TIMEOUT_S,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f04a9d2", + "metadata": {}, + "outputs": [], + "source": [ + "doc_path = documents[0]\n", + "extracted_document = call_extraction_api(requests.Session(), doc_path)[\"detail\"]\n", + "document = \"\\n\".join(extracted_document[\"document\"])\n", + "print(document)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb930b69", + "metadata": {}, + "outputs": [], + "source": [ + "extracted_documents = []\n", + "\n", + "for doc_path in tqdm(documents):\n", + " session = requests.Session()\n", + " extracted_document = call_extraction_api(session, doc_path)\n", + " extracted_documents.append(\n", + " {\n", + " \"doc_path\": os.path.basename(doc_path),\n", + " **extracted_document,\n", + " }\n", + " )\n", + " session.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ce7ea84c", + "metadata": {}, + "outputs": [], + "source": [ + "len(extracted_documents)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a4fee57", + "metadata": {}, + "outputs": [], + "source": [ + "extracted_documents[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a11c0245", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter out empty extracted documents\n", + "extracted_documents = [\n", + " doc for doc in extracted_documents if len(doc[\"detail\"][\"document\"])\n", + "]\n", + "len(extracted_documents)" + ] + }, + { + "cell_type": "markdown", + "id": "e509e467", + "metadata": {}, + "source": [ + "## LLMs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00604dcd", + "metadata": {}, + "outputs": [], + "source": [ + "MODELS = [\n", + " # Llama\n", + " \"llama3.1:8b\",\n", + " \"llama3.2:3b\",\n", + " # Gemma\n", + " \"gemma3:4b\",\n", + " \"gemma3n:e4b\",\n", + " \"gemma3:12b\",\n", + " # Qwen\n", + " \"qwen3:8b\",\n", + " \"qwen3:14b\",\n", + " # Phi\n", + " \"phi3:3.8b\",\n", + " \"phi4:14b\",\n", + " # DeepSeek\n", + " \"deepseek-r1:8b\",\n", + " \"deepseek-r1:14b\",\n", + " # GPT\n", + " \"gpt-oss:20b\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "7669a65c", + "metadata": {}, + "source": [ + "### System Prompts" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1adce13", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompts = load_yaml(\"/resources/llm/summarization.yml\")[\"system-prompts\"]\n", + "system_prompts" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dfe7899", + "metadata": {}, + "outputs": [], + "source": [ + "# Function to get chat response from the model\n", + "def get_chat_response(\n", + " system_prompt: str,\n", + " user_prompt: str,\n", + " model: str = \"llama3.2:3b\",\n", + " options: dict = {\"num_ctx\": 16_384, \"num_predict\": 4096},\n", + ") -> ChatResponse:\n", + " \"\"\"\n", + " Get chat response from the model.\n", + "\n", + " Args:\n", + " system_prompt (str): The system prompt.\n", + " user_prompt (str): The prompt from the user.\n", + " model (str, optional): The model to use. Defaults to \"llama3.2:3b\".\n", + " options (dict, optional): The options for the chat.\n", + " Defaults to {\"num_ctx\": 16_384, \"num_predict\": 4096}.\n", + "\n", + " Returns:\n", + " ChatResponse: The response from the chat.\n", + " \"\"\"\n", + " response = ollama.chat(\n", + " model=model,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options=options,\n", + " )\n", + "\n", + " return response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c8016bd", + "metadata": {}, + "outputs": [], + "source": [ + "iter_documents = iter(extracted_documents)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8677785", + "metadata": {}, + "outputs": [], + "source": [ + "document = next(iter_documents)\n", + "document = \"\\n\".join(document[\"detail\"][\"document\"])\n", + "print(document)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "034bd672", + "metadata": {}, + "outputs": [], + "source": [ + "# Define system prompt and get chat response\n", + "system_prompt = system_prompts[\"baseline\"]\n", + "\n", + "# Get chat response\n", + "chat_response = get_chat_response(system_prompt, document)\n", + "print(chat_response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "918dc835", + "metadata": {}, + "outputs": [], + "source": [ + "# Define system prompt and get chat response\n", + "system_prompt = system_prompts[\"task-specific\"]\n", + "\n", + "# Get chat response\n", + "chat_response = get_chat_response(system_prompt, document)\n", + "print(chat_response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f4b55f3", + "metadata": {}, + "outputs": [], + "source": [ + "# Define prompt parameters\n", + "INFORMATION = \"\"\"\n", + " - Hechos relevantes (qué ocurrió).\n", + " - Actores clave (quiénes están involucrados).\n", + " * Denunciante(s).\n", + " * Denunciado(s).\n", + " * Testigo(s).\n", + " * Juez(es).\n", + " * Abogado(s).\n", + " * Fiscal(es).\n", + " * Perito(s).\n", + " * Otros (especificar).\n", + " - Cronología (cuándo ocurrieron los hechos y eventos procesales importantes).\n", + " - Lugar(es) (dónde ocurrieron los hechos y eventos procesales importantes).\n", + " - Fundamentos (normas legales, precedentes judiciales, doctrinas aplicadas).\n", + " - Decisión (resultado/medida adoptada).\n", + "\"\"\"\n", + "\n", + "ENTITIES = \"\"\"\n", + " - \"BANCO\": Nombre de una entidad bancaria, pública o privada.\n", + " - \"CBU\": Código Bancario Uniforme (22 dígitos) de una cuenta.\n", + " - \"CORREO_ELECTRONICO\": Dirección de correo electrónico.\n", + " - \"CUIJ\": Código Único de Identificación Jurídica de causas judiciales.\n", + " - \"CUIT_CUIL\": Número de CUIT o CUIL de una persona física o jurídica.\n", + " - \"DIRECCION\": Dirección específica (calle, número, intersección de calles y/o avenidas, código postal, etc.).\n", + " - \"DNI\": Número de Documento Nacional de Identidad u otro documento identificatorio.\n", + " - \"EDAD\": Edad explícita de una persona.\n", + " - \"ESTUDIOS\": Nivel o institución educativa que permita identificar a la persona (ej. \"primario incompleto\", \"secundario completo\", \"Licenciado en…\").\n", + " - \"FECHA\": Fecha completa o parcial (día, mes y/o año).\n", + " - \"LINK\": Enlace o URL a una página web.\n", + " - \"LOC\": Localización geográfica específica (país, provincia, estado, ciudad, localidad, barrio, etc.).\n", + " - \"MARCA_AUTOMOVIL\": Marca de un vehículo (ej. Toyota, Ford, Suzuki, etc.).\n", + " - \"NACIONALIDAD\": Nacionalidad de una persona (ej. \"argentino\", \"brasileña\").\n", + " - \"NUM_ACTUACION\": Número identificatorio de una actuación administrativa o contravencional.\n", + " - \"NUM_CAJA_AHORRO\": Número completo de una caja de ahorro o cuenta bancaria.\n", + " - \"NUM_EXPEDIENTE\": Número de expediente judicial o administrativo.\n", + " - \"NUM_MATRICULA\": Número de matrícula profesional o académica.\n", + " - \"PATENTE_DOMINIO\": Patente o dominio de un vehículo.\n", + " - \"PER\": Nombre y apellido(s) de una persona física. Los nombres inicializados (ej., \"M.T.G\") y los apodos (ej., \"el Gato\") también cuentan como información sensible a anonimizar.\n", + " - \"TELEFONO\": Número telefónico (fijo o celular).\n", + "\"\"\"\n", + "\n", + "\n", + "# Define system prompt\n", + "system_prompt = system_prompts[\"template\"].format(\n", + " information_to_extract=INFORMATION,\n", + " entities_to_identify=ENTITIES,\n", + ")\n", + "\n", + "print(system_prompt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3efcf14f", + "metadata": {}, + "outputs": [], + "source": [ + "# Get chat response\n", + "chat_response = get_chat_response(system_prompt, document)\n", + "Markdown(chat_response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92f8d337", + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate_chat_response(\n", + " system_prompt: str,\n", + " user_prompt: str,\n", + " model: str = \"llama3.2:3b\",\n", + " options: dict = {\"num_ctx\": 16_384, \"num_predict\": 4096},\n", + ") -> dict:\n", + " \"\"\"\n", + " Get chat response from the model and evaluate tokens and runtime metrics.\n", + "\n", + " Args:\n", + " system_prompt (str): The system prompt.\n", + " user_prompt (str): The prompt from the user.\n", + " model (str, optional): The model to use. Defaults to \"llama3.2:3b\".\n", + " options (dict, optional): The options for the chat.\n", + " Defaults to {\"num_ctx\": 16_384, \"num_predict\": 4096}.\n", + "\n", + " Returns:\n", + " dict: A dictionary containing:\n", + " - response: The content of the response\n", + " - model: The model name\n", + " - input_tokens: Number of tokens in the prompt (prompt_eval_count)\n", + " - input_duration_ms: Time taken to process input tokens in milliseconds\n", + " - output_tokens: Number of tokens in the output (eval_count)\n", + " - output_duration_ms: Time taken to generate output tokens in milliseconds\n", + " - total_tokens: Total number of tokens processed\n", + " - total_duration_ms: Total duration of the request in milliseconds\n", + " - tokens_per_second: Rate of token generation (output tokens / output duration)\n", + " \"\"\"\n", + " start = time.time()\n", + "\n", + " # Get the chat response\n", + " response = get_chat_response(\n", + " user_prompt=user_prompt,\n", + " model=model,\n", + " system_prompt=system_prompt,\n", + " options=options,\n", + " )\n", + "\n", + " end = time.time()\n", + "\n", + " # Extract metrics\n", + " input_tokens = (\n", + " response.prompt_eval_count if hasattr(response, \"prompt_eval_count\") else None\n", + " )\n", + " output_tokens = response.eval_count if hasattr(response, \"eval_count\") else None\n", + " total_tokens = None\n", + "\n", + " # If total_tokens is still None but we have input and output tokens\n", + " if total_tokens is None and input_tokens is not None and output_tokens is not None:\n", + " total_tokens = input_tokens + output_tokens\n", + "\n", + " # Extract durations and convert nanoseconds to milliseconds\n", + " input_duration_ms = None\n", + " if hasattr(response, \"prompt_eval_duration\"):\n", + " input_duration_ms = response.prompt_eval_duration / 1_000_000 # ns to ms\n", + "\n", + " output_duration_ms = None\n", + " if hasattr(response, \"eval_duration\"):\n", + " output_duration_ms = response.eval_duration / 1_000_000 # ns to ms\n", + "\n", + " model_total_duration_ms = None\n", + " if hasattr(response, \"total_duration\"):\n", + " model_total_duration_ms = response.total_duration / 1_000_000 # ns to ms\n", + "\n", + " # Calculate the total duration from our timer (this will include network latency)\n", + " measured_duration_ms = (end - start) * 1000\n", + "\n", + " # Calculate tokens per second for generation\n", + " tokens_per_second = None\n", + " if (\n", + " output_tokens is not None\n", + " and output_duration_ms is not None\n", + " and output_duration_ms > 0\n", + " ):\n", + " tokens_per_second = output_tokens / (output_duration_ms / 1000)\n", + "\n", + " return {\n", + " \"model\": model,\n", + " \"options\": options,\n", + " \"system_prompt\": system_prompt,\n", + " \"user_prompt\": user_prompt,\n", + " \"chat_response\": response.message.content,\n", + " \"input_tokens\": input_tokens,\n", + " \"input_duration_ms\": input_duration_ms,\n", + " \"output_tokens\": output_tokens,\n", + " \"output_duration_ms\": output_duration_ms,\n", + " \"total_tokens\": total_tokens,\n", + " \"model_duration_ms\": model_total_duration_ms,\n", + " \"measured_duration_ms\": measured_duration_ms,\n", + " \"tokens_per_second\": tokens_per_second,\n", + " \"raw_response\": response.model_dump(), # Include the full response object for reference\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fce8d0f", + "metadata": {}, + "outputs": [], + "source": [ + "# Test our new evaluation function\n", + "test_result = evaluate_chat_response(\n", + " system_prompt=system_prompts[\"task-specific\"],\n", + " user_prompt=document,\n", + ")\n", + "\n", + "# Display the metrics\n", + "print(f\"Response from {test_result['model']}:\")\n", + "print(\"-\" * 50)\n", + "print(test_result[\"raw_response\"])\n", + "print(\"-\" * 50)\n", + "print(f\"Input tokens: {test_result['input_tokens']}\")\n", + "print(f\"Input processing time: {test_result['input_duration_ms']:.2f} ms\")\n", + "print(f\"Output tokens: {test_result['output_tokens']}\")\n", + "print(f\"Output generation time: {test_result['output_duration_ms']:.2f} ms\")\n", + "print(f\"Total tokens: {test_result['total_tokens']}\")\n", + "print(f\"Model reported duration: {test_result['model_duration_ms']:.2f} ms\")\n", + "print(f\"Measured duration: {test_result['measured_duration_ms']:.2f} ms\")\n", + "print(f\"Generation speed: {test_result['tokens_per_second']:.2f} tokens/second\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68a55804", + "metadata": {}, + "outputs": [], + "source": [ + "errors = []\n", + "results = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "868fbd4c", + "metadata": {}, + "outputs": [], + "source": [ + "DEVICE = \"cuda\"\n", + "\n", + "results = load_json(f\"./summarization-benchmark-results-{DEVICE}.json\")\n", + "results[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ca81c50", + "metadata": {}, + "outputs": [], + "source": [ + "len(results)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72c190b3", + "metadata": {}, + "outputs": [], + "source": [ + "for extracted_document in tqdm(\n", + " extracted_documents, total=len(extracted_documents), desc=\"Evaluating documents\"\n", + "):\n", + " doc_path = os.path.basename(extracted_document[\"doc_path\"])\n", + " document = \"\\n\".join(extracted_document[\"detail\"][\"document\"])\n", + "\n", + " if not document.strip():\n", + " print(f\"Skipping empty document: {doc_path}\")\n", + " continue\n", + "\n", + " for model in MODELS:\n", + " for system_prompt_type in system_prompts:\n", + " print(\n", + " f\"Evaluating {doc_path} with model {model} and prompt {system_prompt_type}\"\n", + " )\n", + " if any(\n", + " r\n", + " for r in results\n", + " if r[\"doc_path\"] == doc_path\n", + " and r[\"model\"] == model\n", + " and r[\"system_prompt_type\"] == system_prompt_type\n", + " and r[\"device\"] == DEVICE\n", + " ):\n", + " print(\n", + " f\"Skipping {doc_path} with model {model} and prompt {system_prompt_type} on {DEVICE} (already evaluated)\"\n", + " )\n", + " continue\n", + "\n", + " system_prompt = system_prompts[system_prompt_type]\n", + " if system_prompt_type == \"template\":\n", + " system_prompt = system_prompts[\"template\"].format(\n", + " information_to_extract=INFORMATION,\n", + " entities_to_identify=ENTITIES,\n", + " )\n", + "\n", + " try:\n", + " result = evaluate_chat_response(\n", + " system_prompt=system_prompt,\n", + " user_prompt=document,\n", + " model=model,\n", + " )\n", + " result[\"doc_path\"] = doc_path\n", + " result[\"system_prompt_type\"] = system_prompt_type\n", + " result[\"device\"] = DEVICE\n", + " results.append(result)\n", + "\n", + " save_json(\n", + " results,\n", + " f\"./summarization-benchmark-results-{DEVICE}.json\",\n", + " )\n", + "\n", + " except Exception as e:\n", + " print(f\"Error evaluating {doc_path} with model {model}: {e}\")\n", + " errors.append(\n", + " {\n", + " \"doc_path\": doc_path,\n", + " \"model\": model,\n", + " \"system_prompt\": system_prompt,\n", + " \"device\": DEVICE,\n", + " \"error\": str(e),\n", + " }\n", + " )\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1359dec5", + "metadata": {}, + "outputs": [], + "source": [ + "len(errors)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "849850e9", + "metadata": {}, + "outputs": [], + "source": [ + "errors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc9c3cab", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/summarization/02-statistical-analysis.ipynb b/notebooks/experiments/summarization/02-statistical-analysis.ipynb new file mode 100644 index 00000000..aef5a7d9 --- /dev/null +++ b/notebooks/experiments/summarization/02-statistical-analysis.ipynb @@ -0,0 +1,1651 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f5458e0a", + "metadata": {}, + "source": [ + "# Summaries Analysis\n", + "\n", + "This notebook performs both **quantitative** and **qualitative** analyses of the summaries generated by the API, to build a complete picture of model summary performance.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "481c1d32", + "metadata": {}, + "source": [ + "\n", + "\n", + "**1. Quantitative Analysis**\n", + "- Conduct a **statistical and exploratory data analysis** of the API results using Pandas and descriptive statistics. \n", + "- Explore and refine the **visualizations** to better highlight differences and distributions. \n", + "- Compare **model runtimes** (CPU vs GPU) for the same document to assess performance efficiency. \n", + "\n", + "\n", + "\n", + "**1. Qualitative Analysis**\n", + "- **Garbage Detection:** Identify low-quality or nonsensical summaries (e.g., hallucinations or “garbage” outputs). \n", + " These can often be detected as **outliers** in the quantitative performance metrics. \n", + " DeepSeek has shown clear examples of this behavior. \n", + "- **Summary Comparison:** Evaluate the **relative quality and performance** of summaries across different models, linking the findings with the quantitative metrics.\n" + ] + }, + { + "cell_type": "markdown", + "id": "69039406", + "metadata": {}, + "source": [ + "## Quantitative Analysis" + ] + }, + { + "cell_type": "markdown", + "id": "d98633a6", + "metadata": {}, + "source": [ + "#### Load data and function definitions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5be98b6f", + "metadata": {}, + "outputs": [], + "source": [ + "# Libraries imports\n", + "\n", + "import os\n", + "import json\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns" + ] + }, + { + "cell_type": "markdown", + "id": "b4b981af", + "metadata": {}, + "source": [ + "Function definitions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d58de8bc", + "metadata": {}, + "outputs": [], + "source": [ + "# Attempt to identify parameter scale (in billions) from model names\n", + "def get_model_parameter_scale(model_name):\n", + " import re\n", + "\n", + " if not model_name:\n", + " return None\n", + "\n", + " known_params = {\n", + " \"gemma3:12b\": 12,\n", + " \"gemma3:40b\": 40,\n", + " \"gemma3:20b\": 20,\n", + " \"gemma3:22b\": 22,\n", + " \"gemma3:38b\": 38,\n", + " \"llama3.1:8b\": 8,\n", + " \"llama3.2:3b\": 3,\n", + " \"llama3.2:11b\": 11,\n", + " \"deepseek-r1:1.1b\": 1.1,\n", + " \"deepseek-r1:1b\": 1,\n", + " \"phi-3:3.8b\": 3.8,\n", + " \"phi-4:14b\": 14,\n", + " \"qwen3:3.8b\": 3.8,\n", + " \"qwen3:14b\": 14,\n", + " \"qwen3:72b\": 72,\n", + " }\n", + "\n", + " model_name_lower = model_name.lower()\n", + " if model_name_lower in known_params:\n", + " return known_params[model_name_lower]\n", + "\n", + " match = re.search(r\"(\\d+(?:\\.\\d+)?)\\s*b\", model_name_lower)\n", + " if match:\n", + " try:\n", + " return float(match.group(1))\n", + " except ValueError:\n", + " return None\n", + " return None\n", + "\n", + "\n", + "def unique_vals(df, col):\n", + " print(col)\n", + " return print(f\"Total {col}: {(df[col].nunique())} \\n {(df[col].unique())} \\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03ae93cc", + "metadata": {}, + "outputs": [], + "source": [ + "DATA_PATH = \"/resources/data/restricted/summarization\" # Change this to your data path\n", + "cuda_summaries_file = os.path.join(\n", + " DATA_PATH, \"summarization-benchmark-results-cuda.json\"\n", + ")\n", + "cpu_summaries_file = os.path.join(DATA_PATH, \"summarization-benchmark-results-cpu.json\")\n", + "\n", + "with open(cuda_summaries_file, \"r\") as f:\n", + " cuda_summaries = json.load(f)\n", + "with open(cpu_summaries_file, \"r\") as f:\n", + " cpu_summaries = json.load(f)\n", + "\n", + "summaries = cuda_summaries + cpu_summaries\n", + "df = pd.DataFrame(summaries)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64887e36", + "metadata": {}, + "outputs": [], + "source": [ + "df.doc_path.nunique()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9eff2b81", + "metadata": {}, + "outputs": [], + "source": [ + "df.doc_path = df.doc_path.apply(lambda x: os.path.basename(x))\n", + "df.doc_path.nunique()" + ] + }, + { + "cell_type": "markdown", + "id": "9000571b", + "metadata": {}, + "source": [ + "### Descriptive analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4081f62e", + "metadata": {}, + "outputs": [], + "source": [ + "df.head(4)" + ] + }, + { + "cell_type": "markdown", + "id": "bdaddf02", + "metadata": {}, + "source": [ + "#### Prompts visualization/print" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3dd1c488", + "metadata": {}, + "outputs": [], + "source": [ + "import pprint\n", + "\n", + "unique = (\n", + " df[[\"system_prompt\", \"system_prompt_type\"]].drop_duplicates().reset_index(drop=True)\n", + ")\n", + "\n", + "for _, row in unique.iterrows():\n", + " print(\"Type:\", row[\"system_prompt_type\"])\n", + " print(\"Prompt:\")\n", + " pprint.pprint(row[\"system_prompt\"])\n", + " print(\"---\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7a99302", + "metadata": {}, + "outputs": [], + "source": [ + "df.info()" + ] + }, + { + "cell_type": "markdown", + "id": "9b7300d5", + "metadata": {}, + "source": [ + "#### Data engineer (ms -> mins)" + ] + }, + { + "cell_type": "markdown", + "id": "f628dc9e", + "metadata": {}, + "source": [ + "Create new columns to mins unit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ed9c4c4", + "metadata": {}, + "outputs": [], + "source": [ + "ms_cols = [c for c in df.columns if c.endswith(\"ms\")]\n", + "ms_min_cols = [[c.replace(\"ms\", \"min\"), c] for c in ms_cols]\n", + "time_cols = [c[0] for c in ms_min_cols]\n", + "\n", + "for cols in ms_min_cols:\n", + " c, ms_col = cols\n", + " df[c] = df[ms_col] / 1000 / 60\n", + "\n", + "df.head(2)\n", + "\n", + "time_cols" + ] + }, + { + "cell_type": "markdown", + "id": "4d25024d", + "metadata": {}, + "source": [ + "#### Describe columns" + ] + }, + { + "cell_type": "markdown", + "id": "33014b1c", + "metadata": {}, + "source": [ + "We describe the float type columns:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61cc141c", + "metadata": {}, + "outputs": [], + "source": [ + "desc = (\n", + " df[\n", + " [\n", + " \"input_tokens\",\n", + " \"output_tokens\",\n", + " \"total_tokens\",\n", + " \"input_duration_min\",\n", + " \"output_duration_min\",\n", + " \"model_duration_min\",\n", + " \"tokens_per_second\",\n", + " ]\n", + " ]\n", + " .describe()\n", + " .T\n", + ")\n", + "print(desc)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6dbb2ba5", + "metadata": {}, + "outputs": [], + "source": [ + "840 / 60" + ] + }, + { + "cell_type": "markdown", + "id": "116603ca", + "metadata": {}, + "source": [ + "We observed that the 75% of the results have model_duration_min <= 7.7 minutes, and the is at list one outlier with model_duration_min = 840 minutes (14 hours)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcd5ccbd", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"model_duration_min\"] > 840]" + ] + }, + { + "cell_type": "markdown", + "id": "7945a14f", + "metadata": {}, + "source": [ + "This is an outlier due the garbage of its output:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f02adf9b", + "metadata": {}, + "outputs": [], + "source": [ + "import pprint\n", + "\n", + "df[df[\"model_duration_min\"] > 840].chat_response.iloc[0][-200:]" + ] + }, + { + "cell_type": "markdown", + "id": "1ddcf0b0", + "metadata": {}, + "source": [ + "Visualization of unique values per main columns:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0ec9e75", + "metadata": {}, + "outputs": [], + "source": [ + "for col in [\n", + " \"model\",\n", + " \"system_prompt\",\n", + " \"device\",\n", + " \"system_prompt_type\",\n", + " \"user_prompt\",\n", + " \"doc_path\",\n", + "]:\n", + " unique_vals(df, col)" + ] + }, + { + "cell_type": "markdown", + "id": "2e1f550d", + "metadata": {}, + "source": [ + "### Model comparison" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3d684ef", + "metadata": {}, + "outputs": [], + "source": [ + "df.groupby(\"model\")[\n", + " [\"tokens_per_second\", \"model_duration_min\", \"total_tokens\"]\n", + "].mean().sort_values(\"tokens_per_second\", ascending=False)\n", + "df.groupby([\"model\", \"device\"])[[\"model_duration_min\", \"tokens_per_second\"]].mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f749026", + "metadata": {}, + "outputs": [], + "source": [ + "# Runtime distribution\n", + "plt.figure(figsize=(8, 5), dpi=50)\n", + "sns.boxplot(data=df, x=\"model\", y=\"model_duration_min\", hue=\"device\")\n", + "plt.xticks(rotation=45)\n", + "plt.title(\"Model Duration by Device\")\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "86d0a9a8", + "metadata": {}, + "source": [ + "Since deepseek-r1:8b has a duration one or two orders of magnitude longer compared to other models, we removed it from the visualization to avoid scaling issues:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "646c45d5", + "metadata": {}, + "outputs": [], + "source": [ + "df_to_plot = df[df[\"model\"] != \"deepseek-r1:8b\"]\n", + "# Runtime distribution\n", + "plt.figure(figsize=(8, 5), dpi=100)\n", + "sns.boxplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"device\")\n", + "plt.xticks(rotation=45)\n", + "plt.title(\"Tokens per second by device\")\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ceada36", + "metadata": {}, + "outputs": [], + "source": [ + "df_to_plot = df[df[\"model\"] != \"deepseek-r1:8b\"]\n", + "# Runtime distribution\n", + "plt.figure(figsize=(8, 5), dpi=100)\n", + "sns.boxplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"system_prompt_type\")\n", + "plt.xticks(rotation=45)\n", + "plt.title(\"Model Duration by device\")\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30d74a25", + "metadata": {}, + "outputs": [], + "source": [ + "df_to_plot = df[df[\"model\"] != \"deepseek-r1:8b\"]\n", + "# Runtime distribution\n", + "plt.figure(figsize=(8, 5), dpi=100)\n", + "sns.boxplot(\n", + " data=df_to_plot, x=\"model\", y=\"model_duration_min\", hue=\"system_prompt_type\"\n", + ")\n", + "plt.xticks(rotation=45)\n", + "plt.title(\"Model Duration by device\")\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3b6df54f", + "metadata": {}, + "source": [ + "It can be seen how much better cuda performs over cpu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fd45e41", + "metadata": {}, + "outputs": [], + "source": [ + "# Throughput\n", + "plt.figure(figsize=(8, 5), dpi=100)\n", + "sns.barplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"system_prompt_type\")\n", + "plt.xticks(rotation=45)\n", + "plt.grid(True)\n", + "plt.title(\"Throughput (tokens/sec) by Model and Prompt Type\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60c2cbb3", + "metadata": {}, + "outputs": [], + "source": [ + "# Throughput\n", + "plt.figure(figsize=(8, 5), dpi=100)\n", + "sns.barplot(\n", + " data=df_to_plot, x=\"model\", y=\"model_duration_min\", hue=\"system_prompt_type\"\n", + ")\n", + "plt.xticks(rotation=45)\n", + "plt.grid(True)\n", + "plt.ylabel(\"Model duration (min)\")\n", + "plt.title(\"Tokens per second by Model and Prompt Type\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8c681b9", + "metadata": {}, + "outputs": [], + "source": [ + "# Token ratios\n", + "plt.figure(figsize=(8, 5), dpi=100)\n", + "df_to_plot[\"compression_ratio\"] = df_to_plot[\"output_tokens\"] / df[\"input_tokens\"]\n", + "sns.violinplot(data=df_to_plot, x=\"model\", y=\"compression_ratio\")\n", + "plt.xticks(rotation=45)\n", + "plt.grid(True)\n", + "plt.title(\"Output/Input Token Ratio (Compression Quality Proxy)\")\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "8bc2cfc2", + "metadata": {}, + "source": [ + "A nivel " + ] + }, + { + "cell_type": "markdown", + "id": "36a2746e", + "metadata": {}, + "source": [ + "The mean imput tokens by model are:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb3d4078", + "metadata": {}, + "outputs": [], + "source": [ + "for filter, df_filtered in [\n", + " (\"all\", df),\n", + " (\"cuda\", df[df[\"device\"] == \"cuda\"]),\n", + " (\"cpu\", df[df[\"device\"] == \"cpu\"]),\n", + "]:\n", + " fig, axes = plt.subplots(ncols=2, figsize=(10, 5))\n", + " label = filter if filter != \"all\" else filter + \" devices\"\n", + "\n", + " sns.barplot(\n", + " data=df_filtered,\n", + " x=\"model\",\n", + " y=\"tokens_per_second\",\n", + " ax=axes[0],\n", + " order=vc.index if \"vc\" in globals() else None,\n", + " label=label,\n", + " )\n", + " axes[0].set_title(\"Generation Speed by Model\")\n", + " axes[0].set_ylabel(\"Tokens per Second\")\n", + " axes[0].tick_params(axis=\"x\", rotation=45)\n", + " axes[0].grid(axis=\"y\", linestyle=\"--\", alpha=0.7)\n", + "\n", + " sns.barplot(\n", + " data=df_filtered,\n", + " x=\"model\",\n", + " y=\"measured_duration_min\",\n", + " ax=axes[1],\n", + " order=vc.index if \"vc\" in globals() else None,\n", + " label=label,\n", + " )\n", + " axes[1].set_title(\"Total Processing Time by Model\")\n", + " axes[1].set_ylabel(\"Duration (min)\")\n", + " axes[1].tick_params(axis=\"x\", rotation=45)\n", + " axes[1].grid(axis=\"y\", linestyle=\"--\", alpha=0.7)\n", + "\n", + " plt.tight_layout()\n", + " plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "a7afc1f2", + "metadata": {}, + "source": [ + "### Gap Analysis (CPU - CUDA)" + ] + }, + { + "cell_type": "markdown", + "id": "ea239bb0", + "metadata": {}, + "source": [ + "#### Time Gap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7218eea4", + "metadata": {}, + "outputs": [], + "source": [ + "# time gap between CPU and CUDA (cpu_ms - cuda_ms)\n", + "pivot = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", + "pivot.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50c879eb", + "metadata": {}, + "outputs": [], + "source": [ + "# time gap between CPU and CUDA (cpu_ms - cuda_ms)\n", + "pivot = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", + "if {\"cpu\", \"cuda\"}.issubset(pivot.columns):\n", + " # ensure no missing values and work on a copy\n", + " pivot = pivot.reindex(pivot.index).fillna(0).copy()\n", + "\n", + " # reset index so 'model' becomes a column\n", + " pivot = pivot.reset_index().rename(columns={\"index\": \"model\"})\n", + "\n", + " # ensure numeric types and compute difference (values are already in minutes)\n", + " pivot[\"cpu\"] = pd.to_numeric(pivot[\"cpu\"], errors=\"coerce\").fillna(0)\n", + " pivot[\"cuda\"] = pd.to_numeric(pivot[\"cuda\"], errors=\"coerce\").fillna(0)\n", + " pivot[\"cpu_minus_cuda_min\"] = pivot[\"cpu\"] - pivot[\"cuda\"]\n", + "\n", + " # build a 1-D list of model names ordered by the gap\n", + " order = list(pivot.sort_values(\"cpu_minus_cuda_min\", ascending=False)[\"model\"])\n", + "\n", + " plt.figure(figsize=(12, 5))\n", + " sns.barplot(data=pivot, x=\"model\", y=\"cpu_minus_cuda_min\", order=order)\n", + " plt.axhline(0, color=\"k\", linestyle=\"--\", linewidth=0.8)\n", + " plt.xticks(rotation=45)\n", + " plt.ylabel(\"CPU - CUDA duration (min)\")\n", + " plt.title(\"Time gap between CPU and CUDA by model (CPU - CUDA)\")\n", + " plt.tight_layout()\n", + " plt.show()\n", + "else:\n", + " print(\n", + " \"Not enough data to compute CPU vs CUDA gap (missing 'cpu' or 'cuda' device rows).\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "583b4638", + "metadata": {}, + "source": [ + "#### Token per second gap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ea8df17", + "metadata": {}, + "outputs": [], + "source": [ + "# time gap between CPU and CUDA (cpu_ms - cuda_ms)\n", + "pivot = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", + "if {\"cpu\", \"cuda\"}.issubset(pivot.columns):\n", + " # ensure no missing values and work on a copy\n", + " pivot = pivot.reindex(pivot.index).fillna(0).copy()\n", + "\n", + " # reset index so 'model' becomes a column\n", + " pivot = pivot.reset_index().rename(columns={\"index\": \"model\"})\n", + "\n", + " # ensure numeric types and compute difference (values are already in minutes)\n", + " pivot[\"cpu\"] = pd.to_numeric(pivot[\"cpu\"], errors=\"coerce\").fillna(0)\n", + " pivot[\"cuda\"] = pd.to_numeric(pivot[\"cuda\"], errors=\"coerce\").fillna(0)\n", + " pivot[\"cpu_minus_cuda_min\"] = pivot[\"cpu\"] - pivot[\"cuda\"]\n", + "\n", + " # build a 1-D list of model names ordered by the gap\n", + " order = list(pivot.sort_values(\"cpu_minus_cuda_min\", ascending=False)[\"model\"])\n", + "\n", + " plt.figure(figsize=(12, 5))\n", + " sns.barplot(data=pivot, x=\"model\", y=\"cpu_minus_cuda_min\", order=order)\n", + " plt.axhline(0, color=\"k\", linestyle=\"--\", linewidth=0.8)\n", + " plt.xticks(rotation=45)\n", + " plt.ylabel(\"CPU - CUDA tokens per second gap\")\n", + " plt.title(\"Token per second gap between CPU and CUDA by model (CPU - CUDA)\")\n", + " plt.tight_layout()\n", + " plt.show()\n", + "else:\n", + " print(\n", + " \"Not enough data to compute CPU vs CUDA gap (missing 'cpu' or 'cuda' device rows).\"\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bebfb4d2", + "metadata": {}, + "outputs": [], + "source": [ + "df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "797cfad4", + "metadata": {}, + "outputs": [], + "source": [ + "pivot_token = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", + "pivot_dur = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", + "\n", + "# prepare a list of models to keep consistent ordering (try to reuse vc order if present)\n", + "models = list(vc.index) if \"vc\" in globals() else sorted(df[\"model\"].unique())\n", + "\n", + "\n", + "# build a safe summary even if one device is missing\n", + "def safe_gap(pivot, models):\n", + " # ensure index contains all models and missing values filled with 0\n", + " p = (\n", + " pivot.reindex(models)\n", + " .fillna(0)\n", + " .copy()\n", + " .reset_index()\n", + " .rename(columns={\"index\": \"model\"})\n", + " )\n", + " # coerce numeric and add cpu/cuda columns if missing\n", + " p[\"cpu\"] = pd.to_numeric(p.get(\"cpu\", 0), errors=\"coerce\").fillna(0)\n", + " p[\"cuda\"] = pd.to_numeric(p.get(\"cuda\", 0), errors=\"coerce\").fillna(0)\n", + " return p\n", + "\n", + "\n", + "p_token = safe_gap(pivot_token, models)\n", + "p_dur = safe_gap(pivot_dur, models)\n", + "\n", + "model_summary = pd.DataFrame(\n", + " {\n", + " \"model\": p_token[\"model\"],\n", + " \"gap_input_tokens\": p_token[\"cpu\"] - p_token[\"cuda\"],\n", + " # convert minutes gap to seconds\n", + " \"gap_total_duration_min\": (p_dur[\"cpu\"] - p_dur[\"cuda\"]),\n", + " }\n", + ")\n", + "\n", + "# bubble size: proportional to number of samples per model (scaled for plotting)\n", + "counts = df.groupby(\"model\").size().reindex(model_summary[\"model\"]).fillna(1)\n", + "model_summary[\"bubble_size\"] = (counts / counts.max()) * 400 + 50\n", + "\n", + "# ensure columns have expected dtypes\n", + "model_summary[\"gap_input_tokens\"] = pd.to_numeric(\n", + " model_summary[\"gap_input_tokens\"], errors=\"coerce\"\n", + ").fillna(0)\n", + "model_summary[\"gap_total_duration_min\"] = pd.to_numeric(\n", + " model_summary[\"gap_total_duration_min\"], errors=\"coerce\"\n", + ").fillna(0)\n", + "\n", + "model_summary.head()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c00d6e92", + "metadata": {}, + "outputs": [], + "source": [ + "pivot_token = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", + "pivot_dur = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", + "\n", + "# prepare a list of models to keep consistent ordering (try to reuse vc order if present)\n", + "models = list(vc.index) if \"vc\" in globals() else sorted(df[\"model\"].unique())\n", + "\n", + "\n", + "# build a safe summary even if one device is missing\n", + "def safe_gap(pivot, models):\n", + " # ensure index contains all models and missing values filled with 0\n", + " p = (\n", + " pivot.reindex(models)\n", + " .fillna(0)\n", + " .copy()\n", + " .reset_index()\n", + " .rename(columns={\"index\": \"model\"})\n", + " )\n", + " # coerce numeric and add cpu/cuda columns if missing\n", + " p[\"cpu\"] = pd.to_numeric(p.get(\"cpu\", 0), errors=\"coerce\").fillna(0)\n", + " p[\"cuda\"] = pd.to_numeric(p.get(\"cuda\", 0), errors=\"coerce\").fillna(0)\n", + " return p\n", + "\n", + "\n", + "p_token = safe_gap(pivot_token, models)\n", + "p_dur = safe_gap(pivot_dur, models)\n", + "\n", + "model_summary = pd.DataFrame(\n", + " {\n", + " \"model\": p_token[\"model\"],\n", + " \"gap_tokens\": p_token[\"cpu\"] - p_token[\"cuda\"],\n", + " # convert minutes gap to seconds\n", + " \"gap_total_duration_min\": (p_dur[\"cpu\"] - p_dur[\"cuda\"]),\n", + " }\n", + ")\n", + "\n", + "# bubble size: proportional to number of samples per model (scaled for plotting)\n", + "counts = df.groupby(\"model\").size().reindex(model_summary[\"model\"]).fillna(1)\n", + "model_summary[\"bubble_size\"] = (counts / counts.max()) * 400 + 50\n", + "\n", + "# ensure columns have expected dtypes\n", + "model_summary[\"gap_tokens\"] = pd.to_numeric(\n", + " model_summary[\"gap_tokens\"], errors=\"coerce\"\n", + ").fillna(0)\n", + "model_summary[\"gap_total_duration_min\"] = pd.to_numeric(\n", + " model_summary[\"gap_total_duration_min\"], errors=\"coerce\"\n", + ").fillna(0)\n", + "\n", + "model_summary.head()\n", + "\n", + "\n", + "fig, ax = plt.subplots(figsize=(12, 6))\n", + "ax.scatter(\n", + " model_summary[\"gap_tokens\"],\n", + " model_summary[\"gap_total_duration_min\"],\n", + " # s=model_summary[\"bubble_size\"],\n", + " alpha=0.7,\n", + " color=\"#1f77b4\",\n", + " edgecolors=\"black\",\n", + ")\n", + "for _, row in model_summary.iterrows():\n", + " ax.text(\n", + " row[\"gap_tokens\"],\n", + " row[\"gap_total_duration_min\"],\n", + " row[\"model\"],\n", + " fontsize=9,\n", + " ha=\"left\",\n", + " va=\"bottom\",\n", + " alpha=0.9,\n", + " )\n", + "ax.set_ylabel(\"Average Total Duration Gap (min)\")\n", + "ax.set_xlabel(\"Average Tokens per second\")\n", + "# ax.set_ylabel(\"Average Total Duration (s)\")\n", + "ax.grid(True, linestyle=\"--\", alpha=0.6)\n", + "ax.set_title(\"CPU vs CUDA Performance Gap by Model\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8542204f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c42e753", + "metadata": {}, + "outputs": [], + "source": [ + "def make_summary(df):\n", + " pivot_token = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", + " pivot_dur = (\n", + " df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", + " )\n", + "\n", + " # prepare a list of models to keep consistent ordering\n", + " models = sorted(df[\"model\"].unique())\n", + "\n", + " # build a safe summary even if one device is missing\n", + " def safe_gap(pivot, models):\n", + " p = (\n", + " pivot.reindex(models)\n", + " .fillna(0)\n", + " .copy()\n", + " .reset_index()\n", + " .rename(columns={\"index\": \"model\"})\n", + " )\n", + " p[\"cpu\"] = pd.to_numeric(p.get(\"cpu\", 0), errors=\"coerce\").fillna(0)\n", + " p[\"cuda\"] = pd.to_numeric(p.get(\"cuda\", 0), errors=\"coerce\").fillna(0)\n", + " return p\n", + "\n", + " p_token = safe_gap(pivot_token, models)\n", + " p_dur = safe_gap(pivot_dur, models)\n", + "\n", + " model_summary = pd.DataFrame(\n", + " {\n", + " \"model\": p_token[\"model\"],\n", + " # CUDA - CPU gap for tokens per second\n", + " \"gap_tokens\": p_token[\"cuda\"] - p_token[\"cpu\"],\n", + " # CUDA - CPU gap for total duration (minutes)\n", + " \"gap_total_duration_min\": p_dur[\"cuda\"] - p_dur[\"cpu\"],\n", + " }\n", + " )\n", + "\n", + " # add parameter scale and bubble size\n", + " model_summary[\"param_scale\"] = model_summary[\"model\"].apply(\n", + " get_model_parameter_scale\n", + " )\n", + " model_summary[\"param_scale\"] = pd.to_numeric(\n", + " model_summary[\"param_scale\"], errors=\"coerce\"\n", + " ).fillna(0)\n", + "\n", + " # counts = df.groupby('model').size().reindex(model_summary['model']).fillna(1)\n", + " # model_summary['bubble_size'] = (counts / counts.max()) * 400 + 50\n", + "\n", + " # ensure numeric types and no NaNs\n", + " model_summary[\"gap_tokens\"] = pd.to_numeric(\n", + " model_summary[\"gap_tokens\"], errors=\"coerce\"\n", + " ).fillna(0)\n", + " model_summary[\"gap_total_duration_min\"] = pd.to_numeric(\n", + " model_summary[\"gap_total_duration_min\"], errors=\"coerce\"\n", + " ).fillna(0)\n", + "\n", + " return model_summary\n", + "\n", + "\n", + "model_summary = make_summary(df)\n", + "model_summary.head()\n", + "\n", + "model_summary.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9fef1bba", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# generate a unique color per model\n", + "palette = sns.color_palette(n_colors=len(model_summary))\n", + "model_summary.sort_values(\"param_scale\", inplace=True)\n", + "model_colors = dict(zip(model_summary[\"model\"], palette))\n", + "\n", + "fig, ax = plt.subplots(figsize=(12, 6))\n", + "i = 1\n", + "# plot bubbles with model-specific colors\n", + "for _, row in model_summary.iterrows():\n", + " color = model_colors[row[\"model\"]]\n", + " ax.scatter(\n", + " row[\"param_scale\"], # row[\"gap_total_duration_min\"],\n", + " row[\"gap_tokens\"],\n", + " s=row[\"param_scale\"] * 100,\n", + " alpha=0.7,\n", + " color=color,\n", + " edgecolors=\"black\",\n", + " linewidth=0.7,\n", + " )\n", + " ax.text(\n", + " row[\"param_scale\"] + (0.5 * (-1) ** i), # row[\"gap_total_duration_min\"]\n", + " row[\"gap_tokens\"],\n", + " row[\"model\"],\n", + " fontsize=9,\n", + " ha=\"left\",\n", + " va=\"bottom\",\n", + " alpha=1,\n", + " fontweight=\"bold\",\n", + " color=color,\n", + " )\n", + " i += 1\n", + "\n", + " # add a label for the bubble size\n", + "\n", + "ax.set_ylabel(\"Average Tokens per Second\")\n", + "ax.set_xlabel(\"Parameter Scale (Billion Parameters)\")\n", + "ax.grid(True, linestyle=\"--\", alpha=0.6)\n", + "ax.set_title(\"CUDA - CPU Gap Token per Second by Model Parameter Scale per Model\")\n", + "\n", + "# optional legend showing color per model\n", + "handles = [\n", + " plt.Line2D(\n", + " [0],\n", + " [0],\n", + " marker=\"o\",\n", + " color=\"w\",\n", + " label=model,\n", + " markerfacecolor=color,\n", + " markersize=8,\n", + " markeredgecolor=\"black\",\n", + " )\n", + " for model, color in model_colors.items()\n", + "]\n", + "ax.legend(handles=handles, title=\"Model\", loc=\"best\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cdfdde71", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# generate a unique color per model\n", + "palette = sns.color_palette(n_colors=len(model_summary))\n", + "model_summary.sort_values(\"param_scale\", inplace=True)\n", + "model_colors = dict(zip(model_summary[\"model\"], palette))\n", + "\n", + "fig, ax = plt.subplots(figsize=(12, 6), dpi=100)\n", + "i = 1\n", + "# plot bubbles with model-specific colors\n", + "for _, row in model_summary.iterrows():\n", + " color = model_colors[row[\"model\"]]\n", + " ax.scatter(\n", + " row[\"param_scale\"], # row[\"gap_total_duration_min\"],\n", + " row[\"gap_total_duration_min\"],\n", + " s=row[\"param_scale\"] * 100,\n", + " alpha=0.7,\n", + " color=color,\n", + " edgecolors=\"black\",\n", + " linewidth=0.7,\n", + " )\n", + " ax.text(\n", + " row[\"param_scale\"] + (0.5 * (-1) ** i), # row[\"gap_total_duration_min\"]\n", + " row[\"gap_total_duration_min\"],\n", + " row[\"model\"],\n", + " fontsize=9,\n", + " ha=\"left\",\n", + " va=\"bottom\",\n", + " alpha=1,\n", + " fontweight=\"bold\",\n", + " color=color,\n", + " )\n", + " i += 1\n", + "\n", + " # add a label for the bubble size\n", + "\n", + "ax.set_ylabel(\"Average Total Duration Gap (min)\")\n", + "ax.set_xlabel(\"Parameter Scale (Billion Parameters)\")\n", + "ax.grid(True, linestyle=\"--\", alpha=0.6)\n", + "ax.set_title(\"CUDA - CPU Gap Token per Second by Model Parameter Scale per Model\")\n", + "\n", + "# optional legend showing color per model\n", + "handles = [\n", + " plt.Line2D(\n", + " [0],\n", + " [0],\n", + " marker=\"o\",\n", + " color=\"w\",\n", + " label=model,\n", + " markerfacecolor=color,\n", + " markersize=8,\n", + " markeredgecolor=\"black\",\n", + " )\n", + " for model, color in model_colors.items()\n", + "]\n", + "ax.legend(handles=handles, title=\"Model\", loc=\"best\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36d4c49b", + "metadata": {}, + "outputs": [], + "source": [ + "(\n", + " df.iloc[0].output_tokens / df.iloc[0].output_duration_ms\n", + ") * 1000 # it is tokens_per_second" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8666d95d", + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.utils.json_data import load_json, save_json\n", + "\n", + "save_json(\n", + " summaries,\n", + " DATA_PATH + \"summaries.json\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eae7c0e6", + "metadata": {}, + "outputs": [], + "source": [ + "df.iloc[0].input_duration_ms + df.iloc[0].output_duration_ms # total duration in ms" + ] + }, + { + "cell_type": "markdown", + "id": "8d4847a7", + "metadata": {}, + "source": [ + "## Qualitative Analysis" + ] + }, + { + "cell_type": "markdown", + "id": "4c19dacc", + "metadata": {}, + "source": [ + "### Garbage Detection - Outliers " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0bba4ac3", + "metadata": {}, + "outputs": [], + "source": [ + "df.describe()" + ] + }, + { + "cell_type": "markdown", + "id": "600d3507", + "metadata": {}, + "source": [ + "max output_tokens is 163840, same value as max input token, so, it is truncated. We will see examples" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cfce3c0", + "metadata": {}, + "outputs": [], + "source": [ + "df_truncated = df[df[\"output_tokens\"] > 16000]\n", + "print(\"Truncated summary analysis: \\ndoc paths: \\n\", df_truncated.doc_path.unique())\n", + "print(\"Models: \\n\", df_truncated.model.unique())\n", + "df_truncated.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77a9fe88", + "metadata": {}, + "outputs": [], + "source": [ + "df_truncated.groupby([\"model\", \"doc_path\", \"system_prompt_type\"]).count()" + ] + }, + { + "cell_type": "markdown", + "id": "fc67ba29", + "metadata": {}, + "source": [ + "11 of 12 outputs truncated correspond to deepseek-r1:8b model, and the other one to phi3:3.8b. We will see the hallucinations bellow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "687db5cd", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 6))\n", + "sns.boxplot(data=df, x=\"model\", y=\"model_duration_ms\")\n", + "plt.xticks(rotation=45)\n", + "plt.title(\"Runtime Variability Across Documents\")\n", + "plt.ylabel(\"Model Duration (ms)\")\n", + "plt.grid(True, alpha=0.4)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fba9021e", + "metadata": {}, + "outputs": [], + "source": [ + "corr = df_to_plot[\n", + " [\n", + " \"input_tokens\",\n", + " \"output_tokens\",\n", + " \"model_duration_ms\",\n", + " \"tokens_per_second\",\n", + " \"compression_ratio\",\n", + " ]\n", + "].corr()\n", + "sns.heatmap(corr, annot=True, cmap=\"coolwarm\", center=0)\n", + "plt.title(\"Correlation Between Metrics\")\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95666d38", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 6))\n", + "sns.boxplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"device\")\n", + "plt.xticks(rotation=45)\n", + "plt.title(\"Tokens per Second Variability Across Documents\")\n", + "plt.ylabel(\"Tokens per Second\")\n", + "plt.grid(True, alpha=0.4)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dce57b6a", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 6))\n", + "sns.boxplot(data=df_to_plot, x=\"model\", y=\"model_duration_min\")\n", + "plt.xticks(rotation=45)\n", + "plt.title(\"Runtime Variability Across Documents\")\n", + "plt.ylabel(\"Model Duration (min)\")\n", + "plt.grid(True, alpha=0.4)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "19b0ed7e", + "metadata": {}, + "source": [ + "### Analysis by document" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a4f0af7", + "metadata": {}, + "outputs": [], + "source": [ + "df_cuda = df[df[\"device\"] == \"cuda\"]\n", + "plt.figure(figsize=(10, 6))\n", + "sns.boxplot(data=df_cuda, x=\"doc_path\", y=\"tokens_per_second\", hue=\"model\")\n", + "plt.xticks(rotation=90)\n", + "plt.title(\"Tokens per second by Document and Model - CUDA\")\n", + "plt.grid(True)\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c4f88ac", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(10, 6))\n", + "sns.boxplot(data=df, x=\"doc_path\", y=\"tokens_per_second\", hue=\"model\")\n", + "plt.xticks(rotation=90)\n", + "plt.title(\"Throughput by Document and Model\")\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc30cd3b", + "metadata": {}, + "outputs": [], + "source": [ + "order = sorted(df[\"model\"].unique())\n", + "order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9ef6921", + "metadata": {}, + "outputs": [], + "source": [ + "i = 1\n", + "for doc in df[\"doc_path\"].unique():\n", + " df_doc = df[df[\"doc_path\"] == doc]\n", + " doc_name = doc.split(\"/\")[-1]\n", + " plt.figure(figsize=(10, 6))\n", + " sns.boxplot(\n", + " data=df_doc, x=\"model\", y=\"tokens_per_second\", hue=\"device\", order=order\n", + " )\n", + " plt.xticks(rotation=90)\n", + " plt.grid(True)\n", + " plt.title(f\"Fig. {i}: {doc_name}\")\n", + " plt.tight_layout()\n", + " i += 1\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8977b125", + "metadata": {}, + "source": [ + "#### Summary comparison (same document)" + ] + }, + { + "cell_type": "markdown", + "id": "31ba2818", + "metadata": {}, + "source": [ + "##### cuda vs llama (same model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c76d769", + "metadata": {}, + "outputs": [], + "source": [ + "last_N = 1000\n", + "document = \"1- FLORES ABARCA, Francisco Alexander s 149 bis J-01-00369422-0-2022-1 Sala Feria (II) nnya vict do apela pp.pdf\"\n", + "model = \"llama3.2:3b\"\n", + "devices = [\"cuda\", \"cpu\"]\n", + "df_doc = df[df[\"doc_path\"] == document]\n", + "for d in devices:\n", + " df_filtered = df_doc[\n", + " (df[\"model\"] == model)\n", + " & (df[\"device\"] == d)\n", + " & (df[\"system_prompt_type\"] == \"template\")\n", + " ]\n", + " print(\n", + " f\"--------------------- Document: {document}, device= {d}, model= {model}---------------------\"\n", + " )\n", + " text = df_filtered[\"chat_response\"].astype(str).fillna(\"\")[-last_N:].values[0]\n", + " print(text)\n", + " print(\"\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "d1afcf25", + "metadata": {}, + "source": [ + " **Interpretation**\n", + "- Running the same model (Llama 3.2:3b) and same prompt/document in cuda is 5–6× faster than in cpu\n", + "- Despite the speedup, output content remains consistent, with both versions extracting similar entities and structure.\n", + "- The CUDA output finishes much faster but tends to include slightly more redundant or verbose entities (e.g., repeated names), suggesting minor decoding variability due to parallelization.\n", + "- The CPU output is slower but more stable and concise, with slightly better formatting consistency.\n", + "\n", + "This comparison confirms that CUDA significantly improves throughput without major semantic drift, though some token-level differences may appear." + ] + }, + { + "cell_type": "markdown", + "id": "699a16de", + "metadata": {}, + "source": [ + "##### gemma vs llama (cuda)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ad69da3", + "metadata": {}, + "outputs": [], + "source": [ + "last_N = 1000\n", + "document = \"aymurai - ejemplo 02.docx\"\n", + "models = [\"phi3:3.8b\", \"gemma3:12b\"]\n", + "df_doc = df[df[\"doc_path\"] == document]\n", + "for m in models:\n", + " df_filtered = df_doc[\n", + " (df[\"model\"] == m)\n", + " & (df[\"device\"] == \"cuda\")\n", + " & (df[\"system_prompt_type\"] == \"template\")\n", + " ]\n", + " print(\n", + " f\"--------------------- Document: {document}, model= {m}---------------------\"\n", + " )\n", + " text = df_filtered[\"chat_response\"].astype(str).fillna(\"\")[-last_N:].values[0]\n", + " print(text)\n", + " print(\"\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "5d7a976c", + "metadata": {}, + "source": [ + "- Both models now generate coherent, well-structured summaries capturing key actors, context, and applied laws.\n", + "Phi 3:3.8b stands out for its richer narrative detail , like “Ana Carolina Rodríguez (DNI 34.112.456) y su hija menor M.F.R”\n", + "\n", + "- Gemma 3:12b produces a clearer and more factual summary, focusing on explicit information and structured entities.\n", + "- Phi offers greater completeness and contextual depth, while Gemma achieves higher precision and speed.\n", + "Overall, they show complementary strengths: Phi excels in descriptive depth, Gemma in concise factual extraction." + ] + }, + { + "cell_type": "markdown", + "id": "182387a0", + "metadata": {}, + "source": [ + "##### Same document, phi3 vs. phi4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ea9f690", + "metadata": {}, + "outputs": [], + "source": [ + "df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31fb2d90", + "metadata": {}, + "outputs": [], + "source": [ + "set(df.model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cfece56c", + "metadata": {}, + "outputs": [], + "source": [ + "set(df.doc_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1296370", + "metadata": {}, + "outputs": [], + "source": [ + "df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63a4ef9d", + "metadata": {}, + "outputs": [], + "source": [ + "last_N = 1000\n", + "document = \"aymurai - ejemplo 02.docx\"\n", + "models = [\"phi3:3.8b\", \"phi4:14b\"]\n", + "df_doc = df[df[\"doc_path\"] == document]\n", + "for m in models:\n", + " df_filtered = df_doc[\n", + " (df[\"model\"] == m)\n", + " & (df[\"device\"] == \"cuda\")\n", + " & (df[\"system_prompt_type\"] == \"template\")\n", + " ]\n", + " print(\n", + " f\"--------------------- Document: {document}, model= {m}---------------------\"\n", + " )\n", + " text = df_filtered[\"chat_response\"].astype(str).fillna(\"\")[-last_N:].values[0]\n", + " print(text)\n", + " print(\"\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "e078eef3", + "metadata": {}, + "source": [ + "When comparing phi3:3.8b and phi4:14b on the same document, the improvement in phi4 is evident both in structure and content quality. \n", + "\n", + "While phi3 tends to generate verbose outputs and include unnecessary placeholders (e.g., “not mentioned in the document provided”), phi4 produces concise, relevant, and well-organized information. \n", + "\n", + "Its summaries remain focused on the extracted entities without overgenerating text, which improves readability and coherence. The token usage, shown in Fig. 4, also confirm this efficiency: phi4 requires fewer output tokens while maintaining higher generation speed and precision in content extraction." + ] + }, + { + "cell_type": "markdown", + "id": "aebe3666", + "metadata": {}, + "source": [ + "#### Tokens/sec by document: cuda and cpu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2023483d", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "# Compute mean and std per document/model/device\n", + "df_summary = (\n", + " df.groupby([\"doc_path\", \"model\", \"device\"])[\"tokens_per_second\"]\n", + " .agg([\"mean\", \"std\", \"count\"])\n", + " .reset_index()\n", + ")\n", + "df_summary[\"sem\"] = df_summary[\"std\"] / df_summary[\"count\"] ** 0.5 # standard error\n", + "\n", + "# Sort models for consistent order\n", + "# order = sorted(df_summary['model'].unique())\n", + "\n", + "# --- Plot for each device type ---\n", + "for device in [\"cuda\", \"cpu\"]:\n", + " df_device = df_summary[df_summary[\"device\"] == device]\n", + "\n", + " plt.figure(figsize=(10, 6))\n", + "\n", + " # Plot one line per document\n", + " for doc, df_doc in df_device.groupby(\"doc_path\"):\n", + " doc_name = doc.split(\"/\")[-1][:20]\n", + " plt.errorbar(\n", + " df_doc[\"model\"],\n", + " df_doc[\"mean\"],\n", + " yerr=df_doc[\"sem\"],\n", + " fmt=\"-o\",\n", + " capsize=3,\n", + " alpha=0.7,\n", + " label=doc_name,\n", + " )\n", + "\n", + " plt.xticks(rotation=90)\n", + " plt.xlabel(\"Model\")\n", + " plt.ylabel(\"Tokens per second (mean ± SEM)\")\n", + " plt.title(f\"Performance across documents ({device.upper()})\")\n", + " plt.grid(True, linestyle=\"--\", alpha=0.6)\n", + " plt.legend(\n", + " title=\"Document\", fontsize=\"small\", loc=\"best\"\n", + " ) # bbox_to_anchor=(1.05, 1), loc='upper left')\n", + " plt.tight_layout()\n", + " plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2fbe4fdc", + "metadata": {}, + "source": [ + "**Overall speed:**\n", + "- On CUDA, generation speed ranges from ~10 to 90 tokens/sec — up to 10× faster than CPU.\n", + "- On CPU, all models stay below 13 tokens/sec, showing clear hardware dependency.\n", + " \n", + "**Model comparison:**\n", + "- Llama 3.2:3b consistently achieves the highest speed across both devices, though its summary quality is limited.\n", + "- Phi 3/4 and DeepSeek 1.1b are among the slowest, likely due to heavier contextual reasoning or token complexity — however, DeepSeek often fails to summarize properly, repeating or looping.\n", + "- Gemma 3:12b and Gemma 3:4b show balanced performance, maintaining moderate speed and stable behavior across documents.\n", + "\n", + "**Document variability:**\n", + "- Speed variation across documents is low, indicating that model performance is robust to input differences.\n", + "- Slight dips appear for longer or denser texts, especially in CPU runs.\n", + " \n", + "**Key insight:**\n", + "- CUDA acceleration drastically improves throughput without affecting consistency across documents.\n", + "- Llama remains the best trade-off between speed and reliability, while Phi and Gemma prioritize content quality over raw speed." + ] + }, + { + "cell_type": "markdown", + "id": "0b721074", + "metadata": {}, + "source": [ + "### Output outliers/hallucinations visualizations" + ] + }, + { + "cell_type": "markdown", + "id": "c3a3c149", + "metadata": {}, + "source": [ + "We see the last 100 characters of each output that was truncated (df['output_tokens']>16000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca18869a", + "metadata": {}, + "outputs": [], + "source": [ + "N = 100\n", + "for idx, txt in enumerate(df_truncated[\"chat_response\"].astype(str).fillna(\"\")):\n", + " print(\n", + " f\"+++++++++++++ Truncated summary {idx} (model={df_truncated.iloc[idx].get('model', '')}, system_prompt_type={df_truncated.iloc[idx].get('system_prompt_type', '')}, doc_path={df_truncated.iloc[idx].get('doc_path', '')}) +++++++++++++\"\n", + " )\n", + " text = txt[-N:]\n", + " print(text)\n", + " print(\"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aed870a3", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/summarization/03-summarization-endpoint.ipynb b/notebooks/experiments/summarization/03-summarization-endpoint.ipynb new file mode 100644 index 00000000..059248c0 --- /dev/null +++ b/notebooks/experiments/summarization/03-summarization-endpoint.ipynb @@ -0,0 +1,170 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d636dee3", + "metadata": {}, + "source": [ + "# Summarization API endpoint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53cbedeb", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "\n", + "import requests\n", + "from rich.pretty import pprint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24dfb1f2", + "metadata": {}, + "outputs": [], + "source": [ + "API_BASE_URL = os.getenv(\"AYMURAI_API_BASE_URL\", \"http://localhost:8999\")\n", + "SUMMARIZE_URL = f\"{API_BASE_URL}/llm/summarize\"\n", + "print(f\"POST {SUMMARIZE_URL}\")" + ] + }, + { + "cell_type": "markdown", + "id": "2be189f4", + "metadata": {}, + "source": [ + "## Build request payload" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b547916", + "metadata": {}, + "outputs": [], + "source": [ + "sample_text = \"\"\"\n", + "La inteligencia artificial (IA) ha revolucionado numerosos campos, desde la medicina hasta la industria del entretenimiento,\n", + "y el ámbito de la justicia no es una excepción. La IA tiene el potencial de transformar la forma en que se administran los\n", + "sistemas legales, mejorando la eficiencia y la precisión en la toma de decisiones judiciales. Sin embargo, también plantea\n", + "desafíos éticos y legales significativos que deben ser abordados cuidadosamente.\n", + "\"\"\".strip()\n", + "\n", + "payload = {\n", + " \"text\": sample_text,\n", + " \"model\": \"llama3.2:3b\",\n", + " \"system_prompt\": \"Eres un asistente que resume textos de manera concisa y clara sin inventar información.\",\n", + " \"tokenizer\": None,\n", + " \"options\": {\"num_ctx\": 1024, \"num_predict\": 512, \"stream\": True},\n", + "}\n", + "\n", + "pprint(payload)\n" + ] + }, + { + "cell_type": "markdown", + "id": "aa2adc0d", + "metadata": {}, + "source": [ + "## Call the endpoint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81d6d33f", + "metadata": {}, + "outputs": [], + "source": [ + "def call_summarize(text_payload: dict) -> dict:\n", + " response = requests.post(SUMMARIZE_URL, json=text_payload)\n", + " response.raise_for_status()\n", + " return response.json()\n", + "\n", + "\n", + "result = call_summarize(payload)\n", + "pprint({\"model\": result.get(\"model\"), \"chunks_used\": result.get(\"chunks_used\")})\n", + "print(\"Summary:\")\n", + "print(result.get(\"summary\", \"\"))\n", + "\n", + "print(\"Steps:\")\n", + "pprint(result.get(\"steps\", []))" + ] + }, + { + "cell_type": "markdown", + "id": "d3402133", + "metadata": {}, + "source": [ + "## Streamed call" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aaa18c09", + "metadata": {}, + "outputs": [], + "source": [ + "STREAM_URL = f\"{API_BASE_URL}/llm/summarize/stream\"\n", + "\n", + "\n", + "def stream_summarize(text_payload: dict) -> None:\n", + " with requests.post(STREAM_URL, json=text_payload, stream=True, timeout=120) as resp:\n", + " resp.raise_for_status()\n", + " for raw_line in resp.iter_lines():\n", + " if not raw_line:\n", + " continue\n", + " if not raw_line.startswith(b\"data: \"):\n", + " continue\n", + " event = json.loads(raw_line.split(b\"data: \", 1)[1])\n", + " if event.get(\"type\") == \"token\":\n", + " print(event.get(\"text\", \"\"), end=\"\", flush=True)\n", + " elif event.get(\"type\") == \"summary\":\n", + " print(\"\\n\\n---\\nFinal summary:\\n\")\n", + " print(event.get(\"summary\", \"\"))\n", + " print(\"\\nSteps:\", event.get(\"steps\", []))\n", + " else:\n", + " print(f\"\\n[{event.get('type')}] {event}\")\n", + "\n", + "\n", + "stream_summarize(payload)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d14bc8be", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb b/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb new file mode 100644 index 00000000..b40fb1a2 --- /dev/null +++ b/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb @@ -0,0 +1,955 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "0bf2c8f0", + "metadata": {}, + "outputs": [], + "source": [ + "# !uv add dspy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fab6180c", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1734177", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import dspy\n", + "\n", + "from aymurai.utils.json_data import load_json" + ] + }, + { + "cell_type": "markdown", + "id": "011093f3", + "metadata": {}, + "source": [ + "## Fetch data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d546951", + "metadata": {}, + "outputs": [], + "source": [ + "labels_dir = \"/workspace/notebooks/experiments/entity-disambiguation/entities-reviewed\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dd3dbc2", + "metadata": {}, + "outputs": [], + "source": [ + "filenames = os.listdir(labels_dir)\n", + "filenames[:1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60e2e77a", + "metadata": {}, + "outputs": [], + "source": [ + "filename = filenames[0]\n", + "label_data = load_json(os.path.join(labels_dir, filename))\n", + "label_data" + ] + }, + { + "cell_type": "markdown", + "id": "43684f12", + "metadata": {}, + "source": [ + "## Documents" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2c389b4", + "metadata": {}, + "outputs": [], + "source": [ + "import mimetypes\n", + "import time\n", + "from pathlib import Path\n", + "from typing import Iterable\n", + "\n", + "import requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebc22f54", + "metadata": {}, + "outputs": [], + "source": [ + "BASE_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", + "ENDPOINT = f\"{BASE_URL}/misc/document-extract\"\n", + "DATA_ROOT = Path(\n", + " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78f2d559", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "print(f\"Discovered {len(documents)} documents.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04c89e3b", + "metadata": {}, + "outputs": [], + "source": [ + "def call_extraction_api(\n", + " session: requests.Session, file_path: Path\n", + ") -> dict[str, object]:\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " ENDPOINT,\n", + " files=files,\n", + " timeout=REQUEST_TIMEOUT_S,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08b9ebb8", + "metadata": {}, + "outputs": [], + "source": [ + "doc_path = Path(os.path.join(DATA_ROOT, \"aymurai - ejemplo 01.docx\"))\n", + "extracted_document = call_extraction_api(requests.Session(), doc_path)\n", + "document = \"\\n\".join(extracted_document[\"detail\"][\"document\"])\n", + "print(document)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6f722d5", + "metadata": {}, + "outputs": [], + "source": [ + "references = load_json(os.path.join(labels_dir, filename))\n", + "references" + ] + }, + { + "cell_type": "markdown", + "id": "137a89c6", + "metadata": {}, + "source": [ + "## Evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e9b03a8", + "metadata": {}, + "outputs": [], + "source": [ + "# lm = dspy.LM(\"ollama_chat/gpt-oss:20b\", api_base=\"http://localhost:11434\")\n", + "lm = dspy.LM(\"ollama_chat/qwen3:8b\", api_base=\"http://localhost:11434\")\n", + "dspy.configure(lm=lm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81c419f3", + "metadata": {}, + "outputs": [], + "source": [ + "print(document)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9825ac86", + "metadata": {}, + "outputs": [], + "source": [ + "# qwen3:14b\n", + "summarize = dspy.ChainOfThought(\"document -> summary\")\n", + "response = summarize(document=document)\n", + "print(response.summary)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8b91d03", + "metadata": {}, + "outputs": [], + "source": [ + "summarize = dspy.ChainOfThought(\"document -> summary\")\n", + "response = summarize(document=document)\n", + "print(response.summary)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a17443d0", + "metadata": {}, + "outputs": [], + "source": [ + "print(response.reasoning)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15d30a8d", + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "\n", + "from rapidfuzz import fuzz\n", + "\n", + "\n", + "class AssessSummary(dspy.Signature):\n", + " \"\"\"High-precision assessment of legal summaries with strict grounding requirements.\"\"\"\n", + "\n", + " # Inputs\n", + " document = dspy.InputField(\n", + " desc=\"Verbatim source document. Treat it as the only ground truth; cite exact passages when justifying decisions.\"\n", + " )\n", + " summary = dspy.InputField(\n", + " desc=\"Candidate legal summary whose clarity, factual grounding, and entity coverage will be judged.\"\n", + " )\n", + " references = dspy.InputField(\n", + " desc=\"Ordered list of salient legal entities, citations, or parties expected to appear verbatim in the summary.\"\n", + " )\n", + "\n", + " # Outputs\n", + " language_ok: bool = dspy.OutputField(\n", + " desc=\"Return True only if the input summary is completely written in the same language as the document.\"\n", + " )\n", + " language_reason = dspy.OutputField(\n", + " desc=\"Identify any language mismatches or confirm consistent language use.\"\n", + " )\n", + " clarity_ok: bool = dspy.OutputField(\n", + " desc=\"Return True only if the summary is concise, logically ordered, and understandable without assuming missing context.\"\n", + " )\n", + " clarity_reason = dspy.OutputField(\n", + " desc=\"Briefly highlight phrasing strengths or point out specific sentences that harmed clarity.\"\n", + " )\n", + " factfulness_ok: bool = dspy.OutputField(\n", + " desc=\"Return True only if every claim is explicitly supported by the document. Any unsupported or extrapolated statement must flip this to False.\"\n", + " )\n", + " factfulness_reason = dspy.OutputField(\n", + " desc=\"Quote the exact evidence backing supported claims. Explicitly list each unsupported or hallucinated statement.\"\n", + " )\n", + " coverage_ok: bool = dspy.OutputField(\n", + " desc=\"Return True only if the summary mentions the expected references with correct attributes and relationships.\"\n", + " )\n", + " coverage_reason = dspy.OutputField(\n", + " desc=\"Explain which references were correctly included or omitted, explicitly citing the relevant summary spans.\"\n", + " )\n", + "\n", + "\n", + "@dataclass\n", + "class SummaryScore:\n", + " score: float\n", + " coverage_score: float\n", + " judgement: dspy.Prediction\n", + "\n", + "\n", + "class SummaryMetric(dspy.Module):\n", + " COVERAGE_THRESHOLD = 0.9\n", + "\n", + " def __call__(self, document, references, summary, trace=None):\n", + " coverage_score = self._coverage_score(summary, references)\n", + "\n", + " # with dspy.context(\n", + " # lm=dspy.LM(\"ollama_chat/gpt-oss:20b\", api_base=\"http://localhost:11434\")\n", + " # ):\n", + " # Run assessment model\n", + " judgement = self.predict(\n", + " document=document,\n", + " summary=summary,\n", + " references=references,\n", + " )\n", + "\n", + " if not judgement.language_ok:\n", + " return SummaryScore(\n", + " score=0.0,\n", + " coverage_score=coverage_score,\n", + " judgement=judgement,\n", + " )\n", + "\n", + " score = sum(\n", + " [\n", + " 0.2 * (judgement.clarity_ok or 0),\n", + " 0.2 * (judgement.factfulness_ok or 0),\n", + " 0.6 * (coverage_score * (judgement.coverage_ok or 0)),\n", + " ]\n", + " )\n", + "\n", + " return SummaryScore(\n", + " score=score,\n", + " coverage_score=coverage_score,\n", + " judgement=judgement,\n", + " )\n", + "\n", + " def predict(self, document, summary, references):\n", + " return dspy.Predict(AssessSummary)(\n", + " document=document,\n", + " summary=summary,\n", + " references=references,\n", + " )\n", + "\n", + " def _coverage_score(\n", + " self, summary: str, references, threshold: float | None = None\n", + " ) -> float:\n", + " \"\"\"\n", + " Calculate the coverage score of the summary against the reference entities.\n", + "\n", + " Args:\n", + " summary (str): The summary text to be evaluated for coverage.\n", + " references (list): The list of reference entities expected to be covered in the summary.\n", + " threshold (float | None): The minimum similarity threshold to consider an entity as covered.\n", + " Defaults to COVERAGE_THRESHOLD.\n", + "\n", + " Returns:\n", + " float: The coverage score representing how well the summary covers the reference entities.\n", + " \"\"\"\n", + " if not references:\n", + " return 1.0\n", + "\n", + " threshold = threshold or self.COVERAGE_THRESHOLD\n", + " summary_lower = summary.lower()\n", + " total_score, counted = 0.0, 0\n", + "\n", + " for ref in references:\n", + " aliases = list(ref.get(\"aliases\") or [])\n", + " canonical = ref.get(\"canonical_text\")\n", + " if canonical:\n", + " aliases = [canonical, *aliases]\n", + "\n", + " aliases = set(alias.strip().lower() for alias in aliases if alias)\n", + "\n", + " best = max(\n", + " fuzz.partial_ratio(alias.lower(), summary_lower) / 100.0\n", + " for alias in aliases\n", + " )\n", + " if best < threshold:\n", + " best = 0.0\n", + "\n", + " total_score += best\n", + " counted += 1\n", + "\n", + " return total_score / counted if counted else 0.0\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6607ae5", + "metadata": {}, + "outputs": [], + "source": [ + "metric = SummaryMetric()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f43d5278", + "metadata": {}, + "outputs": [], + "source": [ + "# phi3:14b\n", + "score = metric(document, references, response.summary)\n", + "score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "636d3124", + "metadata": {}, + "outputs": [], + "source": [ + "score = metric(document, references, response.summary)\n", + "score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2e23a09", + "metadata": {}, + "outputs": [], + "source": [ + "summarization_results = load_json(\n", + " DATA_ROOT / \"summarization-benchmark-results-cuda.json\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52a0ef90", + "metadata": {}, + "outputs": [], + "source": [ + "len(summarization_results)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88820925", + "metadata": {}, + "outputs": [], + "source": [ + "summaries = [\n", + " doc\n", + " for doc in summarization_results\n", + " if doc[\"doc_path\"] == os.path.basename(doc_path)\n", + "]\n", + "\n", + "len(summaries)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb08ce45", + "metadata": {}, + "outputs": [], + "source": [ + "from tqdm import tqdm\n", + "\n", + "evaluation_results = []\n", + "\n", + "for summary_entry in tqdm(summaries):\n", + " summary = summary_entry[\"chat_response\"]\n", + " score = metric(document, references, summary)\n", + " evaluation_results.append(\n", + " {\n", + " \"doc_path\": summary_entry[\"doc_path\"],\n", + " \"summary\": summary,\n", + " \"score\": score,\n", + " \"metadata\": {\n", + " \"model\": summary_entry.get(\"model\"),\n", + " \"system_prompt_type\": summary_entry.get(\"system_prompt_type\"),\n", + " \"options\": summary_entry.get(\"options\"),\n", + " },\n", + " }\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "986029ec", + "metadata": {}, + "outputs": [], + "source": [ + "evaluation_results = sorted(\n", + " evaluation_results, key=lambda x: x[\"score\"].score, reverse=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7637aa56", + "metadata": {}, + "outputs": [], + "source": [ + "evaluation_results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2be4980", + "metadata": {}, + "outputs": [], + "source": [ + "# Update the score recomputation\n", + "\n", + "evaluation_results_bkp = evaluation_results.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55177b67", + "metadata": {}, + "outputs": [], + "source": [ + "evaluation_results[-1][\"score\"].score = 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a33d896", + "metadata": {}, + "outputs": [], + "source": [ + "evaluation_results[-1][\"score\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bac3165b", + "metadata": {}, + "outputs": [], + "source": [ + "for eval_result in evaluation_results:\n", + " summary_score = eval_result[\"score\"]\n", + " judgement = summary_score.judgement\n", + "\n", + " score = sum(\n", + " [\n", + " 0.2 * (judgement.clarity_ok or 0),\n", + " 0.2 * (judgement.factfulness_ok or 0),\n", + " 0.6 * (summary_score.coverage_score or 0),\n", + " ]\n", + " )\n", + "\n", + " summary_score.score = score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78790a1b", + "metadata": {}, + "outputs": [], + "source": [ + "evaluation_resultsv = sorted(\n", + " evaluation_results, key=lambda x: x[\"score\"].score, reverse=True\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea3d7601", + "metadata": {}, + "outputs": [], + "source": [ + "evaluation_results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91f48ef6", + "metadata": {}, + "outputs": [], + "source": [ + "lm.history[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff02fd1c", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "71898f8f", + "metadata": {}, + "source": [ + "---\n", + "## Optimization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "831a7eee", + "metadata": {}, + "outputs": [], + "source": [ + "labels_docs_map = {} # Fill in with appropriate mapping of labels to documents" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69fa5ec5", + "metadata": {}, + "outputs": [], + "source": [ + "examples = []\n", + "\n", + "for filename, doc_path in labels_docs_map.items():\n", + " references = load_json(os.path.join(labels_dir, filename))\n", + " extracted_document = call_extraction_api(requests.Session(), Path(doc_path))\n", + " document = \"\\n\".join(extracted_document[\"detail\"][\"document\"])\n", + "\n", + " example = dspy.Example(\n", + " document=document,\n", + " references=references,\n", + " ).with_inputs(\"document\")\n", + " examples.append(example)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "307668d0", + "metadata": {}, + "outputs": [], + "source": [ + "examples[:2]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2403a1c", + "metadata": {}, + "outputs": [], + "source": [ + "# Split examples into simple train/dev partitions\n", + "train_examples = examples[:8]\n", + "dev_examples = examples[8:]\n", + "\n", + "print(f\"Train examples: {len(train_examples)}\")\n", + "print(f\"Dev examples: {len(dev_examples)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe1d3d92", + "metadata": {}, + "outputs": [], + "source": [ + "class CoTSummarizer(dspy.Module):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.cot = dspy.ChainOfThought(\"document -> summary\")\n", + "\n", + " def forward(self, document):\n", + " # references flows through so optimizers can use it in demos/prompts\n", + " return self.cot(document=document)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ce4e7b5", + "metadata": {}, + "outputs": [], + "source": [ + "metric_module = SummaryMetric()\n", + "\n", + "\n", + "def summary_metric(example, prediction, trace=None):\n", + " package = metric_module(\n", + " document=example.document,\n", + " references=example.references,\n", + " summary=prediction.summary,\n", + " trace=trace,\n", + " )\n", + "\n", + " if trace is not None:\n", + " return package.score >= 0.75\n", + "\n", + " return package.score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a194d49a", + "metadata": {}, + "outputs": [], + "source": [ + "evaluate = dspy.Evaluate(\n", + " devset=examples,\n", + " metric=summary_metric,\n", + " num_threads=8,\n", + " display_progress=True,\n", + " display_table=True,\n", + ")\n", + "\n", + "evaluation = evaluate(\n", + " program=CoTSummarizer(),\n", + ")\n", + "\n", + "evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "305f5d0f", + "metadata": {}, + "outputs": [], + "source": [ + "from dspy.teleprompt import COPRO\n", + "\n", + "model_to_generate_prompts = dspy.LM(\n", + " \"ollama_chat/gpt-oss:20b\",\n", + " api_base=\"http://localhost:11434\",\n", + " drop_params=True,\n", + ")\n", + "\n", + "copro = COPRO(\n", + " prompt_model=model_to_generate_prompts,\n", + " metric=summary_metric,\n", + ")\n", + "eval_kwargs = {\"num_threads\": 16, \"display_progress\": True, \"display_table\": 0}\n", + "\n", + "copro_program = copro.compile(\n", + " student=CoTSummarizer(),\n", + " trainset=examples,\n", + " eval_kwargs=eval_kwargs,\n", + ")\n", + "\n", + "copro_program" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "126401a0", + "metadata": {}, + "outputs": [], + "source": [ + "copro_program" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e4b7b2f1", + "metadata": {}, + "outputs": [], + "source": [ + "from dspy.teleprompt import COPRO\n", + "\n", + "model_to_generate_prompts = dspy.LM(\n", + " \"ollama_chat/gpt-oss:20b\",\n", + " api_base=\"http://localhost:11434\",\n", + " drop_params=True,\n", + ")\n", + "\n", + "copro = COPRO(\n", + " prompt_model=model_to_generate_prompts,\n", + " metric=summary_metric,\n", + ")\n", + "eval_kwargs = {\"num_threads\": 16, \"display_progress\": True, \"display_table\": 0}\n", + "\n", + "copro_program = copro.compile(\n", + " student=CoTSummarizer(),\n", + " trainset=examples,\n", + " eval_kwargs=eval_kwargs,\n", + ")\n", + "\n", + "copro_program" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56912cdf", + "metadata": {}, + "outputs": [], + "source": [ + "copro_program" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d604c0e", + "metadata": {}, + "outputs": [], + "source": [ + "evaluate = dspy.Evaluate(\n", + " devset=examples,\n", + " metric=summary_metric,\n", + " num_threads=8,\n", + " display_progress=True,\n", + " display_table=True,\n", + ")\n", + "\n", + "\n", + "evaluation = evaluate(program=copro_program)\n", + "evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6224fc48", + "metadata": {}, + "outputs": [], + "source": [ + "from dspy.teleprompt import MIPROv2\n", + "\n", + "program = CoTSummarizer()\n", + "\n", + "optimizer = MIPROv2(\n", + " prompt_model=model_to_generate_prompts,\n", + " metric=summary_metric,\n", + " auto=\"medium\",\n", + ")\n", + "\n", + "# Optimize program\n", + "print(\"Optimizing program with MIPRO...\")\n", + "optimized_program = optimizer.compile(\n", + " program.deepcopy(),\n", + " trainset=train_examples,\n", + " valset=dev_examples,\n", + " max_bootstrapped_demos=4,\n", + " max_labeled_demos=0,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a262672", + "metadata": {}, + "outputs": [], + "source": [ + "bootstrapped_eval = evaluate(program=optimized_program)\n", + "bootstrapped_eval" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a9e63a6", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml index 0bd35304..9c0dbf08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,6 +92,7 @@ dependencies = [ "python-docx>=1.2.0", "langextract[openai]==1.1.0", "ollama==0.6.1", + "tiktoken==0.12.0", ] [project.urls] @@ -116,6 +117,7 @@ dev = [ "jupyter>=1.1.1", "pip>=24.3.1", "streamlit>=1.21.0", + "dspy>=3.0.4", ] [tool.setuptools.packages.find] diff --git a/resources/llm/summarization.yml b/resources/llm/summarization.yml new file mode 100755 index 00000000..78a502fc --- /dev/null +++ b/resources/llm/summarization.yml @@ -0,0 +1,71 @@ +system-prompts: + baseline: | + Eres un asistente especializado en resumir documentos. + Tu único objetivo es producir un resumen fiel, claro y conciso del texto que recibirás. + No añadas interpretaciones ni conclusiones no presentes en el documento. + + task-specific: | + Eres un analista legal especializado en elaborar resúmenes de resoluciones y documentos jurídicos. + Tu tarea es condensar la información esencial: hechos, cuestiones jurídicas, fundamentos y decisión. + Mantén precisión terminológica y evita inferencias no textuales. Si algún elemento no aparece en el texto, no lo inventes. + + template: | + Eres un asistente especializado en el análisis de documentos judiciales. + Debes elaborar un resumen conciso del texto recibido, priorizando la información más relevante e identificando: + + {information_to_extract} + + Además, cuando el texto lo permita, debes reconocer y listar las siguientes entidades: + + {entities_to_identify} + + # Formato de salida (Markdown) + + ## Resumen del documento + [Descripción narrativa: hechos principales, cuestiones jurídicas, fundamentos legales y decisión.] + + ## Entidades relevantes + [Listado de entidades identificadas, agrupadas por tipo y, cuando sea posible, indicando relaciones o correspondencias.] + +defaults: + information-to-extract: | + - Hechos relevantes (qué ocurrió). + - Actores clave (quiénes están involucrados). + * Denunciante(s). + * Acusado/a(s). + * Testigo/a(s). + * Juez/a(s). + * Abogado/a(s). + * Fiscal(es). + * Perito(s). + * Otros (especificar). + - Cronología (cuándo ocurrieron los hechos y eventos procesales importantes). + - Lugar(es) (dónde ocurrieron los hechos y eventos procesales importantes). + - Fundamentos (normas legales, precedentes judiciales, doctrinas aplicadas). + - Decisión (resultado/medida adoptada). + + entities-to-identify: | + - "BANCO": Nombre de una entidad bancaria, pública o privada. + - "CBU": Código Bancario Uniforme (22 dígitos) de una cuenta. + - "CORREO_ELECTRONICO": Dirección de correo electrónico. + - "CUIJ": Código Único de Identificación Jurídica de causas judiciales. + - "CUIT_CUIL": Número de CUIT o CUIL de una persona física o jurídica. + - "DIRECCION": Dirección específica (calle, número, intersección de calles y/o avenidas, código postal, etc.). + - "DNI": Número de Documento Nacional de Identidad u otro documento identificatorio. + - "EDAD": Edad explícita de una persona. + - "ESTUDIOS": Nivel o institución educativa que permita identificar a la persona (ej. "primario incompleto", "secundario completo", "Licenciado en…"). + - "FECHA": Fecha completa o parcial (día, mes y/o año). + - "IP": Dirección IP (IPv4 o IPv6). + - "LINK": Enlace o URL a una página web. + - "LOC": Localización geográfica específica (país, provincia, estado, ciudad, localidad, barrio, etc.). + - "MARCA_AUTOMOVIL": Marca de un vehículo (ej. Toyota, Ford, Suzuki, etc.). + - "NACIONALIDAD": Nacionalidad de una persona (ej. "argentino", "brasileña"). + - "NOMBRE_ARCHIVO": Nombre completo de un archivo digital (incluyendo extensión). + - "NUM_ACTUACION": Número identificatorio de una actuación administrativa o contravencional. + - "NUM_CAJA_AHORRO": Número completo de una caja de ahorro o cuenta bancaria. + - "NUM_EXPEDIENTE": Número de expediente judicial o administrativo. + - "NUM_MATRICULA": Número de matrícula profesional o académica. + - "PATENTE_DOMINIO": Patente o dominio de un vehículo. + - "PER": Nombre(s) y apellido(s) de una persona física. Los nombres inicializados (ej., "M.T.G") y los apodos (ej., "el Gato") también cuentan como información sensible a anonimizar. + - "TELEFONO": Número telefónico (fijo o celular). + - "USUARIX": Nombre de usuario o "handle" en redes sociales o plataformas digitales. diff --git a/summarization_app/app.py b/summarization_app/app.py new file mode 100755 index 00000000..e5404bc8 --- /dev/null +++ b/summarization_app/app.py @@ -0,0 +1,759 @@ +import json +import re +import textwrap +from pathlib import Path + +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns +import streamlit as st + +# Set page configuration +st.set_page_config( + page_title="Document Summarization Experiments", + page_icon="📄", + layout="wide", +) + + +# Function to load the results +@st.cache_data +def load_data(file_path: str): + try: + with open(file_path, "r") as f: + data = json.load(f) + return data + except Exception as e: + st.error(f"Error loading data: {e}") + return [] + + +# Function to extract unique values from the dataset +def get_unique_values(data, field): + values = set() + for item in data: + if field in item: + values.add(item[field]) + return sorted(list(values)) + + +# Create a helper function to handle None values +def get_sort_key(item, field, sort_order): + value = item.get(field) + # Handle None values by putting them at the end + if value is None: + return float("inf") if sort_order == "Ascending" else float("-inf") + return value + + +# Mapping from UI names to data fields for sorting and plotting +SORT_FIELD_MAP = { + "Model": "model", + "Prompt Type": "system_prompt_type", + "Tokens per Second": "tokens_per_second", + "Duration": "measured_duration_ms", + "Input Tokens": "input_tokens", + "Output Tokens": "output_tokens", + "Device": "device", +} + + +# Attempt to identify parameter scale (in billions) from model names +def get_model_parameter_scale(model_name): + if not model_name: + return None + + known_params = { + "gemma3:12b": 12, + "gemma3:40b": 40, + "gemma3:20b": 20, + "gemma3:22b": 22, + "gemma3:38b": 38, + "llama3.1:8b": 8, + "llama3.2:3b": 3, + "llama3.2:11b": 11, + "deepseek-r1:1.1b": 1.1, + "deepseek-r1:1b": 1, + "phi-3:3.8b": 3.8, + "phi-4:14b": 14, + "qwen3:3.8b": 3.8, + "qwen3:14b": 14, + "qwen3:72b": 72, + } + + model_name_lower = model_name.lower() + if model_name_lower in known_params: + return known_params[model_name_lower] + + match = re.search(r"(\d+(?:\.\d+)?)\s*b", model_name_lower) + if match: + try: + return float(match.group(1)) + except ValueError: + return None + return None + + +# Main function +def main(): + st.title("📄 Document Summarization Experiments") + + # File selector + results_file = st.sidebar.file_uploader("Upload JSON results file", type=["json"]) + + if results_file is not None: + # Load data from uploaded file + content = results_file.getvalue().decode("utf-8") + data = json.loads(content) + else: + # Try to load from the default path + default_path = "./summarization-benchmark-results.json" + if Path(default_path).exists(): + data = load_data(default_path) + st.sidebar.success(f"Loaded data from {default_path}") + else: + st.sidebar.warning("Please upload a JSON results file") + st.stop() + + # Extract unique values for filters + models = get_unique_values(data, "model") + prompt_types = get_unique_values(data, "system_prompt_type") + doc_paths = get_unique_values(data, "doc_path") + doc_filenames = {doc_path: Path(doc_path).name for doc_path in doc_paths} + devices = get_unique_values(data, "device") + + # Create filters in the sidebar + st.sidebar.header("Filters") + + model_options = ["All"] + models + default_models = ["All"] if models else [] + selected_models = st.sidebar.multiselect( + "Select Models", options=model_options, default=default_models + ) + selected_prompt_type = st.sidebar.selectbox( + "Select Prompt Type", ["All"] + prompt_types + ) + selected_doc = st.sidebar.selectbox( + "Select Document", ["All"] + list(doc_filenames.values()) + ) + selected_device = st.sidebar.selectbox("Select Device", ["All"] + devices) + + # Add metrics display option + st.sidebar.header("Display Options") + show_metrics = st.sidebar.checkbox("Show Metrics", value=True) + show_system_prompt = st.sidebar.checkbox("Show System Prompt", value=False) + show_raw_response = st.sidebar.checkbox("Show Raw Response", value=False) + + # Sorting options + st.sidebar.header("Sorting") + sort_by = st.sidebar.selectbox( + "Sort by", + [ + "Default", + "Model", + "Prompt Type", + "Tokens per Second", + "Duration", + "Input Tokens", + "Output Tokens", + "Device", + ], + ) + sort_order = st.sidebar.radio("Sort order", ["Ascending", "Descending"]) + + # Filter data based on selections + filtered_data = data + + if "All" in selected_models: + if len(selected_models) == 1: + selected_models_filter = None + else: + selected_models_filter = { + model for model in selected_models if model != "All" + } + else: + selected_models_filter = set(selected_models) + + if selected_models_filter: + filtered_data = [ + item + for item in filtered_data + if item.get("model") in selected_models_filter + ] + + if selected_prompt_type != "All": + filtered_data = [ + item + for item in filtered_data + if item.get("system_prompt_type") == selected_prompt_type + ] + + if selected_doc != "All": + filtered_data = [ + item + for item in filtered_data + if doc_filenames.get(item.get("doc_path")) == selected_doc + ] + + if selected_device != "All": + filtered_data = [ + item for item in filtered_data if item.get("device") == selected_device + ] + + # Apply sorting + if sort_by != "Default": + field = SORT_FIELD_MAP.get(sort_by) + if field: + # Sort the filtered data + filtered_data = sorted( + filtered_data, + key=lambda item: get_sort_key(item, field, sort_order), + reverse=(sort_order == "Descending"), + ) + + # Display results count + st.write(f"Found {len(filtered_data)} result(s) out of {len(data)} total.") + + # Pagination controls + st.sidebar.header("Pagination") + items_per_page_options = [5, 10, 20, 50, 100, "All"] + + # Create session state for items per page if it doesn't exist + if "items_per_page" not in st.session_state: + st.session_state.items_per_page = 10 + + # Handle the items per page selection + selected_items = st.sidebar.selectbox( + "Results per page", + options=items_per_page_options, + index=items_per_page_options.index( + 10 if 10 in items_per_page_options else items_per_page_options[1] + ), + key="items_per_page_selector", + ) + + # Handle the "All" option + if selected_items == "All": + items_per_page = len(filtered_data) + else: + items_per_page = selected_items + + # Update session state + if st.session_state.items_per_page != items_per_page: + st.session_state.items_per_page = items_per_page + st.session_state.current_page = ( + 1 # Reset to first page when changing items per page + ) + + total_pages = max(1, (len(filtered_data) + items_per_page - 1) // items_per_page) + + # Create session state for pagination if it doesn't exist + if "current_page" not in st.session_state: + st.session_state.current_page = 1 + + # Ensure current page is valid after filtering + if st.session_state.current_page > total_pages: + st.session_state.current_page = 1 + + # Only show pagination controls if there are multiple pages + if total_pages > 1: + col_label, col_input, _ = st.columns([1, 1, 6]) + + with col_label: + st.markdown("**Page**") + st.caption(f"1 - {total_pages}") + + with col_input: + current_page = st.number_input( + "Page selector", + min_value=1, + max_value=total_pages, + value=st.session_state.current_page, + step=1, + label_visibility="collapsed", + ) + st.session_state.current_page = current_page + else: + st.session_state.current_page = 1 + + current_page = st.session_state.current_page + + # Calculate start and end indices for the current page + start_idx = (current_page - 1) * items_per_page + end_idx = min(start_idx + items_per_page, len(filtered_data)) + + # Get the subset of data for the current page + page_data = filtered_data[start_idx:end_idx] + + # Show page information + col1, col2 = st.columns([3, 1]) + with col1: + st.write( + f"Displaying results {start_idx + 1}-{end_idx} of {len(filtered_data)}" + ) + + with col2: + # If we have multiple pages, show a page indicator + if total_pages > 1: + st.write(f"Page {current_page} of {total_pages}") + + # Display the filtered results + if page_data: + for idx, result in enumerate(page_data): + device_tag = ( + f" ({result.get('device', 'N/A')})" if result.get("device") else "" + ) + title = f"{result.get('model', 'N/A')}{device_tag} - [{result.get('system_prompt_type', 'Unknown')}] - {Path(result.get('doc_path', 'N/A')).name}" + + with st.expander(title): + # Display metrics as a table in a clean format + if show_metrics: + col1, col2 = st.columns(2) + + with col1: + st.subheader("Summarization Context") + context_info = { + "Model": result.get("model", "N/A"), + "Device": result.get("device", "N/A"), + "Prompt Type": result.get("system_prompt_type", "Unknown"), + "Document": Path(result.get("doc_path", "N/A")).name, + } + st.table(pd.DataFrame([context_info])) + + with col2: + st.subheader("Performance Metrics") + metrics = { + "Input Tokens": result.get("input_tokens", "N/A"), + "Output Tokens": result.get("output_tokens", "N/A"), + "Total Tokens": result.get("total_tokens", "N/A"), + "Tokens/Second": f"{result.get('tokens_per_second', 0):.2f}", + "Duration (sec)": f"{result.get('measured_duration_ms', 0) / 1000:.2f}", + "Device": result.get("device", "N/A"), + } + st.table(pd.DataFrame([metrics])) + + if show_system_prompt: + st.subheader("System Prompt") + st.caption( + f"Prompt type: **{result.get('system_prompt_type', 'Unknown')}**" + ) + st.code( + result.get("system_prompt", "N/A"), + language="markdown", + ) + + # Display chat response as markdown + st.subheader("Response") + st.markdown(result.get("chat_response", "No response available")) + + # Optional: Display raw response as a JSON + if show_raw_response and "raw_response" in result: + st.subheader("Raw Response") + st.json(result["raw_response"]) + + # Show a summary visualization if more than one result is selected + if len(filtered_data) > 1: + st.header("Summary Visualizations") + + # Create a DataFrame for easier analysis + df = pd.DataFrame(filtered_data) + df = df.loc[:, ~pd.Index(df.columns).duplicated()] + if "model" in df.columns: + df["model"] = df["model"].apply( + lambda value: ( + value + if isinstance(value, str) + else str(value) + if value is not None + else "Unknown" + ) + ) + if "doc_path" in df.columns: + df["document_name"] = df["doc_path"].apply( + lambda p: Path(p).name if isinstance(p, str) else "Unknown" + ) + else: + df["document_name"] = "Unknown" + df["model_params_b"] = df["model"].apply(get_model_parameter_scale) + df["total_duration_sec"] = df["measured_duration_ms"] / 1000 + + # Determine ordering for models in visualizations based on sorting + if "model" in df.columns: + model_values = df["model"].dropna().unique().tolist() + else: + model_values = [] + model_order = model_values.copy() + sort_field = SORT_FIELD_MAP.get(sort_by) + ascending = sort_order == "Ascending" + + if sort_by == "Model": + model_order = sorted(model_values, reverse=not ascending) + elif ( + sort_field + and sort_field in df.columns + and pd.api.types.is_numeric_dtype(df[sort_field]) + ): + model_metric = ( + df.groupby("model")[sort_field] + .mean() + .dropna() + .sort_values(ascending=ascending) + ) + if not model_metric.empty: + model_order = model_metric.index.tolist() + # Append any models missing from the metric (e.g., all NaNs) + remaining_models = [ + model for model in model_values if model not in model_order + ] + model_order.extend(remaining_models) + + if "model" in df.columns and not df.empty: + model_summary = ( + df.groupby("model") + .agg( + avg_input_tokens=("input_tokens", "mean"), + avg_total_duration_ms=("measured_duration_ms", "mean"), + avg_tokens_per_second=("tokens_per_second", "mean"), + runs=("model", "count"), + params_b=("model_params_b", "mean"), + ) + .reset_index() + ) + model_summary["avg_total_duration_sec"] = ( + model_summary["avg_total_duration_ms"] / 1000 + ) + default_param_scale = ( + model_summary["params_b"].median() + if not model_summary["params_b"].dropna().empty + else 1.0 + ) + model_summary["params_b"].fillna(default_param_scale, inplace=True) + model_summary["bubble_size"] = model_summary["params_b"] * 60 + else: + model_summary = pd.DataFrame() + + # Set up tabs for different visualizations + tab1, tab2, tab3, tab4 = st.tabs( + [ + "Performance Metrics", + "Token Usage", + "Prompt Analysis", + "Device Performance", + ] + ) + + with tab1: + col1, col2 = st.columns(2) + + with col1: + # Generation speed by model + fig, ax = plt.subplots(figsize=(10, 6)) + sns.barplot( + data=df, + x="model", + y="tokens_per_second", + order=model_order or None, + ax=ax, + ) + plt.title("Generation Speed by Model") + plt.xticks(rotation=45, ha="right") + plt.ylabel("Tokens per Second") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + with col2: + # Total duration by model + fig, ax = plt.subplots(figsize=(10, 6)) + sns.barplot( + data=df, + x="model", + y="measured_duration_ms", + order=model_order or None, + ax=ax, + ) + plt.title("Total Processing Time by Model") + plt.xticks(rotation=45, ha="right") + plt.ylabel("Duration (ms)") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + st.subheader("Input Tokens vs Total Duration (Average)") + if not model_summary.empty: + fig, ax = plt.subplots(figsize=(12, 6)) + ax.scatter( + model_summary["avg_input_tokens"], + model_summary["avg_total_duration_sec"], + s=model_summary["bubble_size"], + alpha=0.7, + color="#1f77b4", + edgecolors="black", + ) + for _, row in model_summary.iterrows(): + ax.text( + row["avg_input_tokens"], + row["avg_total_duration_sec"], + row["model"], + fontsize=9, + ha="left", + va="bottom", + alpha=0.9, + ) + ax.set_xlabel("Average Input Tokens") + ax.set_ylabel("Average Total Duration (s)") + ax.grid(True, linestyle="--", alpha=0.6) + st.pyplot(fig) + else: + st.info("Not enough data to render the bubble chart.") + + st.subheader("Performance Distributions") + dist_col1, dist_col2 = st.columns(2) + + with dist_col1: + fig, ax = plt.subplots(figsize=(10, 6)) + sns.violinplot( + data=df, + x="model", + y="tokens_per_second", + order=model_order or None, + inner="quartile", + ax=ax, + ) + plt.xticks(rotation=45, ha="right") + plt.ylabel("Tokens per Second") + plt.title("Tokens per Second Distribution by Model") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + with dist_col2: + fig, ax = plt.subplots(figsize=(10, 6)) + sns.violinplot( + data=df, + x="model", + y="total_duration_sec", + order=model_order or None, + inner="quartile", + ax=ax, + ) + plt.xticks(rotation=45, ha="right") + plt.ylabel("Total Duration (s)") + plt.title("Total Duration Distribution by Model") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + with tab2: + col1, col2 = st.columns(2) + + with col1: + # Input vs Output tokens + df_melted = pd.melt( + df, + id_vars=["model"], + value_vars=["input_tokens", "output_tokens"], + var_name="Token Type", + value_name="Token Count", + ) + fig, ax = plt.subplots(figsize=(10, 6)) + sns.barplot( + data=df_melted, + x="model", + y="Token Count", + order=model_order or None, + hue="Token Type", + ax=ax, + ) + plt.title("Input vs Output Tokens by Model") + plt.xticks(rotation=45, ha="right") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + with col2: + # Input processing time vs Output generation time + df_melted = pd.melt( + df, + id_vars=["model"], + value_vars=["input_duration_ms", "output_duration_ms"], + var_name="Processing Stage", + value_name="Duration (ms)", + ) + fig, ax = plt.subplots(figsize=(10, 6)) + sns.barplot( + data=df_melted, + x="model", + y="Duration (ms)", + order=model_order or None, + hue="Processing Stage", + ax=ax, + ) + plt.title("Processing Time Breakdown by Model") + plt.xticks(rotation=45, ha="right") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + with tab3: + col1, col2 = st.columns(2) + + with col1: + # Prompt type distribution + fig, ax = plt.subplots(figsize=(10, 6)) + prompt_counts = ( + df["system_prompt_type"].value_counts().reset_index() + ) + prompt_counts.columns = ["prompt_type", "count"] + sns.barplot(data=prompt_counts, x="prompt_type", y="count", ax=ax) + plt.title("Prompt Type Distribution") + plt.xticks(rotation=45, ha="right") + plt.ylabel("Count") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + with col2: + # Performance by prompt type + fig, ax = plt.subplots(figsize=(10, 6)) + sns.boxplot( + data=df, + x="system_prompt_type", + y="tokens_per_second", + ax=ax, + ) + plt.title("Performance by Prompt Type") + plt.xticks(rotation=45, ha="right") + plt.ylabel("Tokens per Second") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + st.subheader("Document Coverage by Model") + doc_counts = df.assign( + document_label=df["document_name"].apply( + lambda name: ( + textwrap.shorten(name, width=55, placeholder="…") + if isinstance(name, str) + else name + ) + ), + _count=1, + ).pivot_table( + index="document_label", + columns="model", + values="_count", + aggfunc="sum", + fill_value=0, + ) + if not doc_counts.empty: + if model_order: + ordered_cols = [ + col for col in model_order if col in doc_counts.columns + ] + remaining_cols = [ + col for col in doc_counts.columns if col not in ordered_cols + ] + doc_counts = doc_counts[ordered_cols + remaining_cols] + doc_totals = doc_counts.sum(axis=1).sort_values(ascending=False) + max_docs = 25 + if len(doc_totals) > max_docs: + doc_counts = doc_counts.loc[doc_totals.index[:max_docs]] + st.caption( + f"Showing top {max_docs} documents by experiment count." + ) + fig, ax = plt.subplots(figsize=(12, max(6, 0.4 * len(doc_counts)))) + sns.heatmap( + doc_counts, + annot=True, + fmt="d", + cmap="YlGnBu", + linewidths=0.3, + linecolor="white", + cbar=True, + ax=ax, + ) + plt.xlabel("Model") + plt.ylabel("Document") + plt.xticks(rotation=45, ha="right") + plt.yticks(rotation=0) + plt.title("Experiment Coverage per Document and Model") + st.pyplot(fig) + else: + st.info( + "Document coverage heatmap is unavailable with the current selection." + ) + + with tab4: + # Device performance analysis + st.subheader("Performance by Device") + if "device" in df.columns and not df.empty: + # Performance by device (all models combined) + col1, col2 = st.columns(2) + + with col1: + fig, ax = plt.subplots(figsize=(10, 6)) + sns.boxplot(data=df, x="device", y="tokens_per_second", ax=ax) + plt.title("Tokens per Second by Device") + plt.ylabel("Tokens per Second") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + with col2: + fig, ax = plt.subplots(figsize=(10, 6)) + sns.boxplot(data=df, x="device", y="total_duration_sec", ax=ax) + plt.title("Total Duration by Device") + plt.ylabel("Duration (seconds)") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + + # Model performance by device + st.subheader("Model Performance by Device") + fig, ax = plt.subplots(figsize=(12, 8)) + sns.barplot( + data=df, + x="model", + y="tokens_per_second", + hue="device", + order=model_order or None, + ax=ax, + ) + plt.title("Tokens per Second by Model and Device") + plt.xticks(rotation=45, ha="right") + plt.ylabel("Tokens per Second") + plt.grid(axis="y", linestyle="--", alpha=0.7) + plt.legend(title="Device") + st.pyplot(fig) + + # Device usage count + st.subheader("Device Usage Distribution") + device_counts = df["device"].value_counts().reset_index() + device_counts.columns = ["device", "count"] + fig, ax = plt.subplots(figsize=(8, 5)) + sns.barplot(data=device_counts, x="device", y="count", ax=ax) + plt.title("Number of Experiments by Device") + plt.ylabel("Count") + plt.grid(axis="y", linestyle="--", alpha=0.7) + st.pyplot(fig) + else: + st.info( + "Device performance data is unavailable with the current selection." + ) + + else: + st.info("No results match the selected filters.") + + # Add footer with instructions + st.sidebar.markdown("---") + st.sidebar.markdown("### Instructions") + st.sidebar.markdown( + """ + 1. Use the filters above to narrow down results: + - Filter by model, prompt type, document, or device (CPU/CUDA) + - Use multiple selections in the Models filter + 2. Expand result items to view details + 3. Toggle display options to show/hide metrics and raw responses + 4. Explore visualization tabs: + - Performance Metrics: Compare speed and duration between models + - Token Usage: Analyze input and output token distributions + - Prompt Analysis: Review prompt type performance + - Device Performance: Compare CPU vs CUDA performance + 5. Sort results by any metric including device type + """ + ) + + +if __name__ == "__main__": + main() diff --git a/summarization_app/run_streamlit_app.sh b/summarization_app/run_streamlit_app.sh new file mode 100755 index 00000000..0e419441 --- /dev/null +++ b/summarization_app/run_streamlit_app.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Set environment variables to reduce Streamlit watching +export STREAMLIT_SERVER_WATCH_DIRS="false" +export STREAMLIT_SERVER_HEADLESS="true" +export STREAMLIT_SERVER_FILE_WATCHER_TYPE="none" + +# Run Streamlit app +echo "Starting Streamlit app..." +python -m streamlit run app.py diff --git a/uv.lock b/uv.lock index 7f198f0d..6890516d 100644 --- a/uv.lock +++ b/uv.lock @@ -244,6 +244,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, ] +[[package]] +name = "asyncer" +version = "0.0.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209 }, +] + [[package]] name = "attrs" version = "25.1.0" @@ -255,7 +267,7 @@ wheels = [ [[package]] name = "aymurai" -version = "1.1.13.dev16" +version = "1.1.13.dev63" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -292,6 +304,7 @@ dependencies = [ { name = "tensorflow-hub" }, { name = "tensorflow-text" }, { name = "textract" }, + { name = "tiktoken" }, { name = "torch" }, { name = "torchtext" }, { name = "unidecode" }, @@ -301,6 +314,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "dspy" }, { name = "jupyter" }, { name = "matplotlib" }, { name = "nbstripout" }, @@ -348,6 +362,7 @@ requires-dist = [ { name = "tensorflow-hub", specifier = ">=0.16.1" }, { name = "tensorflow-text", specifier = "==2.10.0" }, { name = "textract", specifier = "==1.6.5" }, + { name = "tiktoken", specifier = "==0.12.0" }, { name = "torch", specifier = "==2.9.0" }, { name = "torchtext", specifier = "==0.18.0" }, { name = "unidecode", specifier = "==1.3.8" }, @@ -357,6 +372,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "dspy", specifier = ">=3.0.4" }, { name = "jupyter", specifier = ">=1.1.1" }, { name = "matplotlib", specifier = ">=3.10.0" }, { name = "nbstripout", specifier = ">=0.8.0" }, @@ -377,6 +393,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, ] +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, +] + [[package]] name = "beautifulsoup4" version = "4.8.2" @@ -552,6 +577,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, ] +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -561,6 +595,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "colorlog" +version = "6.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743 }, +] + [[package]] name = "comm" version = "0.2.2" @@ -751,6 +797,40 @@ version = "0.8" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7d/7d/60ee3f2b16d9bfdfa72e8599470a2c1a5b759cb113c6fe1006be28359327/docx2txt-0.8.tar.gz", hash = "sha256:2c06d98d7cfe2d3947e5760a57d924e3ff07745b379c8737723922e7009236e5", size = 2814 } +[[package]] +name = "dspy" +version = "3.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "asyncer" }, + { name = "backoff" }, + { name = "cachetools" }, + { name = "cloudpickle" }, + { name = "diskcache" }, + { name = "gepa" }, + { name = "joblib" }, + { name = "json-repair" }, + { name = "litellm" }, + { name = "magicattr" }, + { name = "numpy" }, + { name = "openai" }, + { name = "optuna" }, + { name = "orjson" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "regex" }, + { name = "requests" }, + { name = "rich" }, + { name = "tenacity" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/18/0042d299cd5e85fdb381568f0cfcc7769122e8f70ea0a2d33e12fd63e705/dspy-3.0.4.tar.gz", hash = "sha256:cb4529df9a91353a16144d9d94ba6ff25f36fc5adfd921f127f4c49d0e309fb8", size = 236376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/52/56eed4828175f48f712a50a994293065afa7cc98cb112992a0b071179b6c/dspy-3.0.4-py3-none-any.whl", hash = "sha256:c0a88c7936f41f6f613ee6ca8cd92e63746ff2bd780e3896615ade7628eb6a6a", size = 285224 }, +] + [[package]] name = "ebcdic" version = "1.1.1" @@ -879,6 +959,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 }, ] +[[package]] +name = "fastuuid" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760 }, + { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748 }, + { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537 }, + { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994 }, + { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003 }, + { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583 }, + { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955 }, + { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763 }, + { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613 }, + { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045 }, + { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122 }, +] + [[package]] name = "filelock" version = "3.17.0" @@ -1044,6 +1143,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/54/0bbe240c6f59ac9209c2356dc65abd209963851569494376a077e55ef98d/gdown-4.6.0-py3-none-any.whl", hash = "sha256:e75c5aa8be8ea1cac642d4793f884339d887ab5e07aaa57fafa16c8a56a0cde5", size = 14400 }, ] +[[package]] +name = "gepa" +version = "0.0.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/f0/fe312ed4405ddc2ca97dc1ce8915c4dd707e413503e6832910ab088fceb6/gepa-0.0.17.tar.gz", hash = "sha256:641ed46f8127618341b66ee82a87fb46a21c5d2d427a5e0b91c850a7f7f64e7f", size = 99816 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/dc/2bc81a01caa887ed58db3c725bebf1e98f37807a4d06c51ecaa85a7cabe0/gepa-0.0.17-py3-none-any.whl", hash = "sha256:0ea98f4179dbc8dd83bdf53494f302e663ee1da8300d086c4cc8ce4aefa4042c", size = 110464 }, +] + [[package]] name = "gitdb" version = "4.0.12" @@ -1540,6 +1648,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, ] +[[package]] +name = "json-repair" +version = "0.54.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/46/d3a4d9a3dad39bb4a2ad16b8adb9fe2e8611b20b71197fe33daa6768e85d/json_repair-0.54.1.tar.gz", hash = "sha256:d010bc31f1fc66e7c36dc33bff5f8902674498ae5cb8e801ad455a53b455ad1d", size = 38555 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/96/c9aad7ee949cc1bf15df91f347fbc2d3bd10b30b80c7df689ce6fe9332b5/json_repair-0.54.1-py3-none-any.whl", hash = "sha256:016160c5db5d5fe443164927bb58d2dfbba5f43ad85719fa9bc51c713a443ab1", size = 29311 }, +] + [[package]] name = "json5" version = "0.10.0" @@ -1929,6 +2046,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594 }, ] +[[package]] +name = "litellm" +version = "1.80.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "fastuuid" }, + { name = "httpx" }, + { name = "importlib-metadata" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "tiktoken" }, + { name = "tokenizers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/b8/357544534bef87dd2858432f3cbd3a0e5cc267caebca5ea86b03618786c5/litellm-1.80.5.tar.gz", hash = "sha256:922791c264845d9ed59e540c8fa74a74d237c1b209568a05ffeacd8b51770deb", size = 11885764 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/af/1d4693746ff9fbbe27a6e7d6394b801acf234e00c83f45ad1cb5bf2eaa6c/litellm-1.80.5-py3-none-any.whl", hash = "sha256:2ac5f4e88cd57ae056e00da8f872e1c2956653750929fba2fd9b007b400fdb77", size = 10671970 }, +] + [[package]] name = "lxml" version = "5.3.0" @@ -1960,6 +2100,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/b2/6a22fb5c0885da3b00e116aee81f0b829ec9ac8f736cd414b4a09413fc7d/lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba", size = 3487557 }, ] +[[package]] +name = "magicattr" +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/7e/76b7e0c391bee7e9273725c29c8fe41c4df62a215ce58aa8e3518baee0bb/magicattr-0.1.6-py2.py3-none-any.whl", hash = "sha256:d96b18ee45b5ee83b09c17e15d3459a64de62d538808c2f71182777dd9dbbbdf", size = 4664 }, +] + [[package]] name = "mako" version = "1.3.8" @@ -2514,6 +2662,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932 }, ] +[[package]] +name = "optuna" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alembic" }, + { name = "colorlog" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "sqlalchemy" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708 }, +] + +[[package]] +name = "orjson" +version = "3.11.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/fe/ed708782d6709cc60eb4c2d8a361a440661f74134675c72990f2c48c785f/orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d", size = 5945188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/30/5aed63d5af1c8b02fbd2a8d83e2a6c8455e30504c50dbf08c8b51403d873/orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1", size = 243870 }, + { url = "https://files.pythonhosted.org/packages/44/1f/da46563c08bef33c41fd63c660abcd2184b4d2b950c8686317d03b9f5f0c/orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44", size = 130622 }, + { url = "https://files.pythonhosted.org/packages/02/bd/b551a05d0090eab0bf8008a13a14edc0f3c3e0236aa6f5b697760dd2817b/orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c", size = 129344 }, + { url = "https://files.pythonhosted.org/packages/87/6c/9ddd5e609f443b2548c5e7df3c44d0e86df2c68587a0e20c50018cdec535/orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23", size = 136633 }, + { url = "https://files.pythonhosted.org/packages/95/f2/9f04f2874c625a9fb60f6918c33542320661255323c272e66f7dcce14df2/orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea", size = 137695 }, + { url = "https://files.pythonhosted.org/packages/d2/c2/c7302afcbdfe8a891baae0e2cee091583a30e6fa613e8bdf33b0e9c8a8c7/orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba", size = 136879 }, + { url = "https://files.pythonhosted.org/packages/c6/3a/b31c8f0182a3e27f48e703f46e61bb769666cd0dac4700a73912d07a1417/orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff", size = 136374 }, + { url = "https://files.pythonhosted.org/packages/29/d0/fd9ab96841b090d281c46df566b7f97bc6c8cd9aff3f3ebe99755895c406/orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac", size = 140519 }, + { url = "https://files.pythonhosted.org/packages/d6/ce/36eb0f15978bb88e33a3480e1a3fb891caa0f189ba61ce7713e0ccdadabf/orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79", size = 406522 }, + { url = "https://files.pythonhosted.org/packages/85/11/e8af3161a288f5c6a00c188fc729c7ba193b0cbc07309a1a29c004347c30/orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827", size = 149790 }, + { url = "https://files.pythonhosted.org/packages/ea/96/209d52db0cf1e10ed48d8c194841e383e23c2ced5a2ee766649fe0e32d02/orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b", size = 140040 }, + { url = "https://files.pythonhosted.org/packages/ef/0e/526db1395ccb74c3d59ac1660b9a325017096dc5643086b38f27662b4add/orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3", size = 135955 }, + { url = "https://files.pythonhosted.org/packages/e6/69/18a778c9de3702b19880e73c9866b91cc85f904b885d816ba1ab318b223c/orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc", size = 131577 }, +] + [[package]] name = "overrides" version = "7.7.0" @@ -3927,6 +4114,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 }, ] +[[package]] +name = "tiktoken" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991 }, + { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798 }, + { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865 }, + { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856 }, + { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308 }, + { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697 }, + { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375 }, +] + [[package]] name = "tinycss2" version = "1.4.0" From 60bf959116d87e024760fe9ab912834dfb45f4c2 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Thu, 27 Nov 2025 14:18:46 +0000 Subject: [PATCH 018/101] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20YAML=20key=20names?= =?UTF-8?q?=20in=20prompt=20defaults=20for=20summarization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/api/endpoints/routers/llm/summarization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aymurai/api/endpoints/routers/llm/summarization.py b/aymurai/api/endpoints/routers/llm/summarization.py index 6e40bc02..a681f594 100644 --- a/aymurai/api/endpoints/routers/llm/summarization.py +++ b/aymurai/api/endpoints/routers/llm/summarization.py @@ -41,8 +41,8 @@ def _load_prompt_defaults(path: Path) -> tuple[str, str]: """ content = load_yaml(str(path)) defaults = content.get("defaults", {}) - info = (defaults.get("information_to_extract") or "").strip() - entities = (defaults.get("entities_to_identify") or "").strip() + info = (defaults.get("information-to-extract") or "").strip() + entities = (defaults.get("entities-to-identify") or "").strip() if not info or not entities: raise RuntimeError("summarization defaults missing in YAML") return info, entities From f0420895c0f1deb17f1b158955695423a82eea52 Mon Sep 17 00:00:00 2001 From: Lio Date: Fri, 5 Dec 2025 15:51:03 -0300 Subject: [PATCH 019/101] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20Restru?= =?UTF-8?q?cture=20USEM=20module=20with=20factory=20pattern=20and=20multip?= =?UTF-8?q?l=E2=80=A6=20(#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Merge dev into main for v1.1.12 (#57) * Update README.md * 🐛 bugfix: Fix XML special character escaping in DocAnonymizer * ➕ build(deps): Add python-docx package * ✨ feat: Add watermark and hyperlink functionality to document anonymization * ✨ feat: Install Archivo font in Dockerfile * 🎨 refactor: Improve Dockerfile structure and comments for clarity * ⏪ revert: Remove Archivo font installation from Dockerfile * 🔖 feat: Update aymurai package version to 1.1.11 in uv.lock * 🐛 Improve get_extension logic to fix document extraction issues on Windows and remove python-magic dependency * 🔧 Update Dockerfile to use 'bullseye' variant for Python images for improved compatibility * 🔧 Update Makefile targets for improved Docker workflow * 🔖 feat: Bump aymurai package version to 1.1.12 * ♻️ Harden get_extension with header scans and zip safeguards * 🔧 Extend document extraction timeout to 30s * 🔧 Refactor Docker workflow to build and push images using docker/build-push-action * 🔧 Fix workflow step order to correctly extract tag name before building Docker images * 🔧 Remove tag extraction step and use github.ref_name directly for Docker image builds * ⏪ Revert Docker workflow to extract tag name and use it for image versioning * Update .github/workflows/build-docker-image.yml * ✏️ Remove incomplete comment --------- * ♻️ refactor: Restructure USEM module with factory pattern and multiple encoder backends - Add BaseSentenceEncoder abstract base class for encoder interface - Implement factory pattern with EncoderType enum and create_encoder function - Add sentence-transformers encoder implementations (DistilUSE, MultilingualMiniLM) - Move TensorFlow implementation to tensorflow_encoder.py - Add lazy loading for encoder implementations via __getattr__ - Add auto-detection for Apple Silicon compatibility (defaults * 🚚 Rename test sentence encoders mac notebook * 📌 Sync dependencies --------- --- aymurai/models/usem/__init__.py | 48 + aymurai/models/usem/base.py | 66 + aymurai/models/usem/core.py | 109 +- aymurai/models/usem/factory.py | 107 + .../usem/sentence_transformers_encoder.py | 124 + aymurai/models/usem/tensorflow_encoder.py | 81 + .../transforms/entity_subcategories/usem.py | 14 +- .../01-test-sentence-encoders-mac.ipynb | 346 +++ pyproject.toml | 5 +- uv.lock | 2556 ++++++++++------- 10 files changed, 2329 insertions(+), 1127 deletions(-) create mode 100644 aymurai/models/usem/base.py create mode 100644 aymurai/models/usem/factory.py create mode 100644 aymurai/models/usem/sentence_transformers_encoder.py create mode 100644 aymurai/models/usem/tensorflow_encoder.py create mode 100644 notebooks/experiments/sentence-encoders/01-test-sentence-encoders-mac.ipynb diff --git a/aymurai/models/usem/__init__.py b/aymurai/models/usem/__init__.py index e69de29b..ce72ab75 100644 --- a/aymurai/models/usem/__init__.py +++ b/aymurai/models/usem/__init__.py @@ -0,0 +1,48 @@ +"""Sentence encoder module with multiple backend implementations. + +Implementations are lazily loaded to avoid importing unavailable dependencies. +Use the factory function `create_encoder()` for automatic backend selection, +or import specific implementations directly from their modules: + + from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder + from aymurai.models.usem.sentence_transformers_encoder import DistilUSEEncoder +""" + +from aymurai.models.usem.base import BaseSentenceEncoder +from aymurai.models.usem.factory import EncoderType, create_encoder, get_encoder +from aymurai.models.usem.core import SentenceRetrieval + + +def __getattr__(name: str): + """Lazy loading of encoder implementations.""" + if name == "TensorFlowUSEEncoder" or name == "USEMQA": + from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder + return TensorFlowUSEEncoder + + if name == "DistilUSEEncoder": + from aymurai.models.usem.sentence_transformers_encoder import DistilUSEEncoder + return DistilUSEEncoder + + if name == "MultilingualMiniLMEncoder": + from aymurai.models.usem.sentence_transformers_encoder import MultilingualMiniLMEncoder + return MultilingualMiniLMEncoder + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +__all__ = [ + # Base interface + "BaseSentenceEncoder", + # Factory + "EncoderType", + "create_encoder", + "get_encoder", + # Implementations (lazy loaded) + "TensorFlowUSEEncoder", + "DistilUSEEncoder", + "MultilingualMiniLMEncoder", + # Backwards compatibility (lazy loaded) + "USEMQA", + # Retrieval + "SentenceRetrieval", +] diff --git a/aymurai/models/usem/base.py b/aymurai/models/usem/base.py new file mode 100644 index 00000000..daf25c69 --- /dev/null +++ b/aymurai/models/usem/base.py @@ -0,0 +1,66 @@ +""" +Abstract base class for sentence encoders. +""" + +import unicodedata +from abc import ABC, abstractmethod +from typing import Iterable, Optional + +import numpy as np + + +class BaseSentenceEncoder(ABC): + """ + Abstract base class defining the interface for sentence encoders. + All encoder implementations must inherit from this class. + """ + + def normalize_text(self, text: str) -> str: + """Normalize text by lowercasing and removing non-alphanumeric characters.""" + text = text.lower() + text = "".join( + char + for char in unicodedata.normalize("NFKD", text) + if char.isalnum() or char.isspace() + ) + return text + + @abstractmethod + def encode( + self, + text_array: list[str], + encoder_type: str, + context_array: Optional[list[str]] = None, + ) -> np.ndarray: + """ + Encode a list of texts into embeddings. + + Args: + text_array: List of texts to encode. + encoder_type: Type of encoder to use ('question_encoder' or 'response_encoder'). + context_array: Optional context for response encoding. + + Returns: + numpy array of embeddings. + """ + pass + + @abstractmethod + def batch_encode( + self, + text_array: Iterable[str], + encoder_type: str, + batch_size: int = 256, + ) -> np.ndarray: + """ + Encode texts in batches. + + Args: + text_array: Iterable of texts to encode. + encoder_type: Type of encoder to use. + batch_size: Number of texts per batch. + + Returns: + numpy array of embeddings. + """ + pass diff --git a/aymurai/models/usem/core.py b/aymurai/models/usem/core.py index 4fbd6746..5a3e6e27 100644 --- a/aymurai/models/usem/core.py +++ b/aymurai/models/usem/core.py @@ -1,97 +1,48 @@ -import unicodedata -from multiprocessing import cpu_count -from typing import Iterable, Optional +"""Sentence encoder implementations and retrieval utilities.""" -import numpy as np -import tensorflow as tf -import tensorflow_hub as hub -import tensorflow_text # noqa -from more_itertools import chunked -from tqdm.auto import tqdm - -from aymurai.logger import get_logger - -logger = get_logger(__name__) - -N_JOBS = cpu_count() - - -class USEMQA: - """ - Base class for USEM (Universal Sentence Encoder Multilingual QA) - """ - - def __init__( - self, - usem_qa_url: str = "https://tfhub.dev/google/universal-sentence-encoder-multilingual-qa/3", - ): - self.embed = hub.load(usem_qa_url) - - def normalize_text(self, text: str) -> str: - text = text.lower() - text = "".join( - char - for char in unicodedata.normalize("NFKD", text) - if char.isalnum() or char.isspace() - ) - return text +from typing import TYPE_CHECKING, Optional - def encode( - self, - text_array: list[str], - encoder_type: str, - context_array: Optional[list[str]] = None, - ) -> np.ndarray: - input_array = [self.normalize_text(text) for text in text_array] - - if encoder_type == "response_encoder": - encoder_params = { - "input": tf.constant(input_array), - "context": tf.constant(context_array), - } - - if encoder_type == "question_encoder": - encoder_params = { - "input": tf.constant(input_array), - } - - encoded = self.embed.signatures[encoder_type](**encoder_params) - encoded = encoded["outputs"] - - return encoded - - def batch_encode( - self, - text_array: Iterable[str], - encoder_type: str, - batch_size: int = 256, - ) -> np.ndarray: - text_chunks = chunked(text_array, batch_size) - - encoded = [ - self.encode(text_chunk, encoder_type) - for text_chunk in tqdm( - text_chunks, - desc="creating USEM vectors...", - total=len(text_array) // batch_size, - ) - ] +import numpy as np - encoded = np.vstack(encoded) +from aymurai.models.usem.base import BaseSentenceEncoder +from aymurai.models.usem.factory import EncoderType, create_encoder - return encoded +# Type hints only - no runtime import +if TYPE_CHECKING: + from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder + from aymurai.models.usem.sentence_transformers_encoder import ( + DistilUSEEncoder, + MultilingualMiniLMEncoder, + ) class SentenceRetrieval: + """Retrieve similar sentences using sentence embeddings.""" + def __init__( self, categories: list[str], response_embeddings_path: str, + encoder: Optional[BaseSentenceEncoder] = None, + encoder_type: Optional[EncoderType] = None, ): - self.usem = USEMQA() + """ + Initialize SentenceRetrieval. + + Args: + categories: List of category labels. + response_embeddings_path: Path to pre-computed embeddings (.npy file). + encoder: Pre-configured encoder instance. If None, creates one using factory. + encoder_type: Type of encoder to create if encoder is None. + If None, reads from SENTENCE_ENCODER_TYPE env var. + """ + self.encoder = encoder if encoder is not None else create_encoder(encoder_type) self.categories = categories self.usem_vectors = self.load_usem_vectors(response_embeddings_path) + # Backwards compatibility alias + self.usem = self.encoder + def load_usem_vectors(self, file_path): usem_vectors = np.load(file_path) return usem_vectors diff --git a/aymurai/models/usem/factory.py b/aymurai/models/usem/factory.py new file mode 100644 index 00000000..7f3ae49a --- /dev/null +++ b/aymurai/models/usem/factory.py @@ -0,0 +1,107 @@ +""" +Factory for creating sentence encoder instances based on configuration. +""" + +import os +import platform +from enum import Enum +from typing import Optional + +from aymurai.logger import get_logger +from aymurai.models.usem.base import BaseSentenceEncoder + +logger = get_logger(__name__) + + +class EncoderType(str, Enum): + """Available encoder implementations.""" + + TENSORFLOW_USE = "tensorflow" + DISTILUSE = "distiluse" + MINILM = "minilm" + AUTO = "auto" + + +def _is_apple_silicon() -> bool: + """Check if running on Apple Silicon.""" + return platform.system() == "Darwin" and platform.machine() == "arm64" + + +def _get_encoder_type_from_env() -> EncoderType: + """Get encoder type from environment variable.""" + env_value = os.getenv("SENTENCE_ENCODER_TYPE", "auto").lower() + try: + return EncoderType(env_value) + except ValueError: + logger.warning( + f"Invalid SENTENCE_ENCODER_TYPE '{env_value}', falling back to 'auto'" + ) + return EncoderType.AUTO + + +def create_encoder( + encoder_type: Optional[EncoderType] = None, + device: Optional[str] = None, +) -> BaseSentenceEncoder: + """ + Factory function to create the appropriate sentence encoder. + + Args: + encoder_type: Type of encoder to create. If None, reads from + SENTENCE_ENCODER_TYPE env var (defaults to 'auto'). + device: Device for sentence-transformers models (ignored for TensorFlow). + + Returns: + An instance of BaseSentenceEncoder. + + Environment Variables: + SENTENCE_ENCODER_TYPE: One of 'tensorflow', 'distiluse', 'minilm', 'auto'. + - 'tensorflow': Use TensorFlow Hub USE-QA (not compatible with Apple Silicon) + - 'distiluse': Use distiluse-base-multilingual-cased-v2 + - 'minilm': Use paraphrase-multilingual-MiniLM-L12-v2 + - 'auto': Auto-detect based on platform (Apple Silicon -> distiluse, else tensorflow) + + Raises: + ImportError: If required dependencies are not available. + ValueError: If an invalid encoder type is specified. + """ + if encoder_type is None: + encoder_type = _get_encoder_type_from_env() + + # Auto-detect based on platform + if encoder_type == EncoderType.AUTO: + if _is_apple_silicon(): + logger.info( + "Apple Silicon detected, using DistilUSE encoder for compatibility" + ) + encoder_type = EncoderType.DISTILUSE + else: + logger.info("Using TensorFlow USE encoder") + encoder_type = EncoderType.TENSORFLOW_USE + + # Create the appropriate encoder + if encoder_type == EncoderType.TENSORFLOW_USE: + from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder + + return TensorFlowUSEEncoder() + + elif encoder_type == EncoderType.DISTILUSE: + from aymurai.models.usem.sentence_transformers_encoder import DistilUSEEncoder + + return DistilUSEEncoder(device=device) + + elif encoder_type == EncoderType.MINILM: + from aymurai.models.usem.sentence_transformers_encoder import ( + MultilingualMiniLMEncoder, + ) + + return MultilingualMiniLMEncoder(device=device) + + else: + raise ValueError(f"Unknown encoder type: {encoder_type}") + + +# Backwards compatibility alias +def get_encoder(**kwargs) -> BaseSentenceEncoder: + """Alias for create_encoder for backwards compatibility.""" + return create_encoder(**kwargs) diff --git a/aymurai/models/usem/sentence_transformers_encoder.py b/aymurai/models/usem/sentence_transformers_encoder.py new file mode 100644 index 00000000..b7cf81b4 --- /dev/null +++ b/aymurai/models/usem/sentence_transformers_encoder.py @@ -0,0 +1,124 @@ +""" +Sentence Transformers encoder implementations. +Compatible with Apple Silicon (M1/M2/M3) via PyTorch MPS backend. +""" + +from typing import Iterable, Optional + +import numpy as np +from sentence_transformers import SentenceTransformer + +from aymurai.logger import get_logger +from aymurai.models.usem.base import BaseSentenceEncoder + +logger = get_logger(__name__) + + +class DistilUSEEncoder(BaseSentenceEncoder): + """ + Knowledge-distilled Universal Sentence Encoder using sentence-transformers. + + This is a PyTorch-based distillation of the original USE multilingual model. + Supports 50+ languages and produces 512-dimensional embeddings. + + Compatible with Apple Silicon via MPS backend. + """ + + MODEL_NAME = "sentence-transformers/distiluse-base-multilingual-cased-v2" + + def __init__(self, device: Optional[str] = None): + """ + Initialize the DistilUSE encoder. + + Args: + device: Device to run the model on. If None, auto-detects + (will use 'mps' on Apple Silicon, 'cuda' if available, else 'cpu'). + """ + self.model = SentenceTransformer(self.MODEL_NAME, device=device) + logger.info(f"Loaded {self.MODEL_NAME} on device: {self.model.device}") + + def encode( + self, + text_array: list[str], + encoder_type: str, + context_array: Optional[list[str]] = None, + ) -> np.ndarray: + """ + Encode texts into embeddings. + + Note: encoder_type and context_array are kept for API compatibility + but are not used since sentence-transformers uses a single encoder. + """ + input_array = [self.normalize_text(text) for text in text_array] + return self.model.encode(input_array, convert_to_numpy=True) + + def batch_encode( + self, + text_array: Iterable[str], + encoder_type: str, + batch_size: int = 256, + ) -> np.ndarray: + """Encode texts in batches with progress bar.""" + text_list = list(text_array) + input_array = [self.normalize_text(text) for text in text_list] + return self.model.encode( + input_array, + batch_size=batch_size, + show_progress_bar=True, + convert_to_numpy=True, + ) + + +class MultilingualMiniLMEncoder(BaseSentenceEncoder): + """ + Multilingual MiniLM encoder using sentence-transformers. + + Higher quality embeddings than DistilUSE with faster inference. + Supports 50+ languages and produces 384-dimensional embeddings. + + Compatible with Apple Silicon via MPS backend. + """ + + MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" + + def __init__(self, device: Optional[str] = None): + """ + Initialize the Multilingual MiniLM encoder. + + Args: + device: Device to run the model on. If None, auto-detects + (will use 'mps' on Apple Silicon, 'cuda' if available, else 'cpu'). + """ + self.model = SentenceTransformer(self.MODEL_NAME, device=device) + logger.info(f"Loaded {self.MODEL_NAME} on device: {self.model.device}") + + def encode( + self, + text_array: list[str], + encoder_type: str, + context_array: Optional[list[str]] = None, + ) -> np.ndarray: + """ + Encode texts into embeddings. + + Note: encoder_type and context_array are kept for API compatibility + but are not used since sentence-transformers uses a single encoder. + """ + input_array = [self.normalize_text(text) for text in text_array] + return self.model.encode(input_array, convert_to_numpy=True) + + def batch_encode( + self, + text_array: Iterable[str], + encoder_type: str, + batch_size: int = 256, + ) -> np.ndarray: + """Encode texts in batches with progress bar.""" + text_list = list(text_array) + input_array = [self.normalize_text(text) for text in text_list] + return self.model.encode( + input_array, + batch_size=batch_size, + show_progress_bar=True, + convert_to_numpy=True, + ) diff --git a/aymurai/models/usem/tensorflow_encoder.py b/aymurai/models/usem/tensorflow_encoder.py new file mode 100644 index 00000000..243e948e --- /dev/null +++ b/aymurai/models/usem/tensorflow_encoder.py @@ -0,0 +1,81 @@ +""" +TensorFlow Hub Universal Sentence Encoder implementation. +""" + +from multiprocessing import cpu_count +from typing import Iterable, Optional + +import numpy as np +import tensorflow as tf +import tensorflow_hub as hub +import tensorflow_text # noqa +from more_itertools import chunked +from tqdm.auto import tqdm + +from aymurai.logger import get_logger +from aymurai.models.usem.base import BaseSentenceEncoder + +logger = get_logger(__name__) + +N_JOBS = cpu_count() + + +class TensorFlowUSEEncoder(BaseSentenceEncoder): + """ + TensorFlow Hub Universal Sentence Encoder Multilingual QA implementation. + + This implementation requires tensorflow-text which is not available on Apple Silicon. + Use SentenceTransformersEncoder for macOS ARM64 compatibility. + """ + + def __init__( + self, + usem_qa_url: str = "https://tfhub.dev/google/universal-sentence-encoder-multilingual-qa/3", + ): + self.embed = hub.load(usem_qa_url) + + def encode( + self, + text_array: list[str], + encoder_type: str, + context_array: Optional[list[str]] = None, + ) -> np.ndarray: + input_array = [self.normalize_text(text) for text in text_array] + + if encoder_type == "response_encoder": + encoder_params = { + "input": tf.constant(input_array), + "context": tf.constant(context_array), + } + + if encoder_type == "question_encoder": + encoder_params = { + "input": tf.constant(input_array), + } + + encoded = self.embed.signatures[encoder_type](**encoder_params) + encoded = encoded["outputs"] + + return encoded + + def batch_encode( + self, + text_array: Iterable[str], + encoder_type: str, + batch_size: int = 256, + ) -> np.ndarray: + text_list = list(text_array) + text_chunks = chunked(text_list, batch_size) + + encoded = [ + self.encode(list(text_chunk), encoder_type) + for text_chunk in tqdm( + text_chunks, + desc="creating USEM vectors...", + total=len(text_list) // batch_size, + ) + ] + + encoded = np.vstack(encoded) + + return encoded diff --git a/aymurai/transforms/entity_subcategories/usem.py b/aymurai/transforms/entity_subcategories/usem.py index c70df207..25645aa6 100644 --- a/aymurai/transforms/entity_subcategories/usem.py +++ b/aymurai/transforms/entity_subcategories/usem.py @@ -3,11 +3,10 @@ from copy import deepcopy import numpy as np -import tensorflow as tf from aymurai.logger import get_logger from aymurai.meta.types import DataItem -from aymurai.models.usem.core import USEMQA +from aymurai.models.usem import create_encoder from aymurai.utils.download import download from aymurai.utils.misc import is_url, get_element from aymurai.meta.pipeline_interfaces import Transform @@ -22,7 +21,7 @@ class USEMSubcategorizer(Transform): Use USEM to retrieve subcategories """ - usem = USEMQA() + usem = create_encoder() def __init__( self, @@ -81,11 +80,10 @@ def retrieve(self, text: str, top_k: int = 10) -> list[str]: """ Retrieve similar sentences using USEM """ - with tf.device(self.device): - query_vector = self.usem.encode( - [text], - encoder_type="question_encoder", - ) + query_vector = self.usem.encode( + [text], + encoder_type="question_encoder", + ) products = np.inner(query_vector, self.usem_vectors)[0] similar_idx = np.flip(products.argsort())[:top_k] diff --git a/notebooks/experiments/sentence-encoders/01-test-sentence-encoders-mac.ipynb b/notebooks/experiments/sentence-encoders/01-test-sentence-encoders-mac.ipynb new file mode 100644 index 00000000..378b1064 --- /dev/null +++ b/notebooks/experiments/sentence-encoders/01-test-sentence-encoders-mac.ipynb @@ -0,0 +1,346 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Test Sentence Encoders on Apple Silicon\n", + "\n", + "This notebook tests the sentence-transformers based encoders that are compatible with Apple Silicon (M1/M2/M3)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import platform\n", + "import time\n", + "\n", + "import numpy as np\n", + "\n", + "# Check platform\n", + "print(f\"Platform: {platform.system()}\")\n", + "print(f\"Machine: {platform.machine()}\")\n", + "print(\n", + " f\"Apple Silicon: {platform.system() == 'Darwin' and platform.machine() == 'arm64'}\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Test DistilUSE Encoder\n", + "\n", + "Knowledge-distilled version of Universal Sentence Encoder. 512-dimensional embeddings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.models.usem import DistilUSEEncoder\n", + "\n", + "distiluse = DistilUSEEncoder()\n", + "print(f\"Model: {distiluse.MODEL_NAME}\")\n", + "print(f\"Device: {distiluse.model.device}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test encoding\n", + "test_sentences = [\n", + " \"El juez dictó sentencia en el caso de violencia de género.\",\n", + " \"La víctima presentó una denuncia por amenazas.\",\n", + " \"Se otorgó una medida de protección a la denunciante.\",\n", + " \"The judge issued a ruling in the gender violence case.\",\n", + " \"The victim filed a complaint for threats.\",\n", + "]\n", + "\n", + "start = time.time()\n", + "embeddings_distiluse = distiluse.encode(test_sentences, encoder_type=\"question_encoder\")\n", + "elapsed = time.time() - start\n", + "\n", + "print(f\"Embeddings shape: {embeddings_distiluse.shape}\")\n", + "print(f\"Encoding time: {elapsed:.3f}s\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Test Multilingual MiniLM Encoder\n", + "\n", + "Higher quality embeddings, faster inference. 384-dimensional embeddings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.models.usem import MultilingualMiniLMEncoder\n", + "\n", + "minilm = MultilingualMiniLMEncoder()\n", + "print(f\"Model: {minilm.MODEL_NAME}\")\n", + "print(f\"Device: {minilm.model.device}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "start = time.time()\n", + "embeddings_minilm = minilm.encode(test_sentences, encoder_type=\"question_encoder\")\n", + "elapsed = time.time() - start\n", + "\n", + "print(f\"Embeddings shape: {embeddings_minilm.shape}\")\n", + "print(f\"Encoding time: {elapsed:.3f}s\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Test Factory Auto-Detection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.models.usem import create_encoder, EncoderType\n", + "\n", + "# Auto-detect (should use DistilUSE on Apple Silicon)\n", + "encoder_auto = create_encoder()\n", + "print(f\"Auto-detected encoder: {type(encoder_auto).__name__}\")\n", + "\n", + "# Explicit selection\n", + "encoder_minilm = create_encoder(EncoderType.MINILM)\n", + "print(f\"Explicit MiniLM encoder: {type(encoder_minilm).__name__}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Semantic Similarity Test\n", + "\n", + "Compare similarity between Spanish and English sentences." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def cosine_similarity(a, b):\n", + " \"\"\"Compute cosine similarity between two vectors.\"\"\"\n", + " return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))\n", + "\n", + "\n", + "def similarity_matrix(embeddings, sentences):\n", + " \"\"\"Compute and display similarity matrix.\"\"\"\n", + " n = len(sentences)\n", + " sim_matrix = np.zeros((n, n))\n", + " for i in range(n):\n", + " for j in range(n):\n", + " sim_matrix[i, j] = cosine_similarity(embeddings[i], embeddings[j])\n", + " return sim_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Compute similarity matrix for DistilUSE\n", + "sim_distiluse = similarity_matrix(embeddings_distiluse, test_sentences)\n", + "\n", + "print(\"DistilUSE Similarity Matrix:\")\n", + "print(\"Sentences:\")\n", + "for i, s in enumerate(test_sentences):\n", + " print(f\" [{i}] {s[:60]}...\" if len(s) > 60 else f\" [{i}] {s}\")\n", + "print()\n", + "print(np.round(sim_distiluse, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Compute similarity matrix for MiniLM\n", + "sim_minilm = similarity_matrix(embeddings_minilm, test_sentences)\n", + "\n", + "print(\"MiniLM Similarity Matrix:\")\n", + "print(np.round(sim_minilm, 3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Cross-lingual Similarity\n", + "\n", + "Test that Spanish-English translation pairs have high similarity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Spanish sentence [0] should be similar to English sentence [3]\n", + "# Spanish sentence [1] should be similar to English sentence [4]\n", + "\n", + "print(\"Cross-lingual similarity (Spanish ↔ English):\")\n", + "print(f\"\\nDistilUSE:\")\n", + "print(\n", + " f\" '{test_sentences[0][:40]}...' ↔ '{test_sentences[3][:40]}...': {sim_distiluse[0, 3]:.3f}\"\n", + ")\n", + "print(\n", + " f\" '{test_sentences[1][:40]}...' ↔ '{test_sentences[4][:40]}...': {sim_distiluse[1, 4]:.3f}\"\n", + ")\n", + "\n", + "print(f\"\\nMiniLM:\")\n", + "print(\n", + " f\" '{test_sentences[0][:40]}...' ↔ '{test_sentences[3][:40]}...': {sim_minilm[0, 3]:.3f}\"\n", + ")\n", + "print(\n", + " f\" '{test_sentences[1][:40]}...' ↔ '{test_sentences[4][:40]}...': {sim_minilm[1, 4]:.3f}\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Batch Encoding Performance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate more sentences for batch test\n", + "batch_sentences = test_sentences * 100 # 500 sentences\n", + "\n", + "print(f\"Batch size: {len(batch_sentences)} sentences\")\n", + "\n", + "# DistilUSE batch\n", + "start = time.time()\n", + "batch_embeddings_distiluse = distiluse.batch_encode(\n", + " batch_sentences, encoder_type=\"question_encoder\", batch_size=64\n", + ")\n", + "elapsed_distiluse = time.time() - start\n", + "print(\n", + " f\"\\nDistilUSE batch encoding: {elapsed_distiluse:.3f}s ({len(batch_sentences) / elapsed_distiluse:.1f} sent/s)\"\n", + ")\n", + "\n", + "# MiniLM batch\n", + "start = time.time()\n", + "batch_embeddings_minilm = minilm.batch_encode(\n", + " batch_sentences, encoder_type=\"question_encoder\", batch_size=64\n", + ")\n", + "elapsed_minilm = time.time() - start\n", + "print(\n", + " f\"MiniLM batch encoding: {elapsed_minilm:.3f}s ({len(batch_sentences) / elapsed_minilm:.1f} sent/s)\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Environment Variable Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test environment variable configuration\n", + "import importlib\n", + "import aymurai.models.usem.factory as factory_module\n", + "\n", + "# Set env var and reload\n", + "os.environ[\"SENTENCE_ENCODER_TYPE\"] = \"minilm\"\n", + "importlib.reload(factory_module)\n", + "from aymurai.models.usem.factory import create_encoder\n", + "\n", + "encoder_from_env = create_encoder()\n", + "print(f\"Encoder from SENTENCE_ENCODER_TYPE=minilm: {type(encoder_from_env).__name__}\")\n", + "\n", + "# Reset to auto\n", + "os.environ[\"SENTENCE_ENCODER_TYPE\"] = \"auto\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "Both `DistilUSEEncoder` and `MultilingualMiniLMEncoder` work on Apple Silicon:\n", + "\n", + "| Model | Embedding Dim | Speed | Quality |\n", + "|-------|---------------|-------|--------|\n", + "| DistilUSE | 512 | Good | Good (USE distillation) |\n", + "| MiniLM | 384 | Faster | Better |\n", + "\n", + "Use `SENTENCE_ENCODER_TYPE=auto` for automatic platform detection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/pyproject.toml b/pyproject.toml index 9c0dbf08..0086a268 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,12 +59,10 @@ dependencies = [ "joblib>=1.4.2", "textract==1.6.5", "datetime_matcher @ git+https://github.com/jedzill4/datetime_matcher", - # "scikit-learn==1.5.2", "jiwer==3.0.5", "datasets>=3.2.0", "unidecode==1.3.8", "sentencepiece==0.2.0", - # "flair @ git+https://github.com/flairNLP/flair@v0.13.1", "flair==0.14.0", "fastapi[standard]>=0.115.6", "uvicorn>=0.34.0", @@ -79,13 +77,14 @@ dependencies = [ "torch==2.9.0", "torchtext==0.18.0", "pytorch-lightning==1.8.3.post1", - "tensorflow_text==2.10.0", # last version compatible with Windows + "tensorflow_text==2.10.0; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'x86_64') or sys_platform == 'win32'", # no ARM64 macOS wheels "psutil==6.1.0", "sqlmodel==0.0.22", "alembic>=1.13.3", "tenacity>=9.0.0", "python-dotenv>=1.0.1", "tensorflow-hub>=0.16.1", + "sentence-transformers>=2.2.0", "pymupdf>=1.25.2", "pymupdf4llm>=0.0.17", "pypandoc>=1.15", diff --git a/uv.lock b/uv.lock index 6890516d..401cc267 100644 --- a/uv.lock +++ b/uv.lock @@ -3,11 +3,11 @@ requires-python = "==3.10.*" [[package]] name = "absl-py" -version = "2.1.0" +version = "2.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/8f/fc001b92ecc467cc32ab38398bd0bfb45df46e7523bf33c2ad22a505f06e/absl-py-2.1.0.tar.gz", hash = "sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff", size = 118055 } +sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/ad/e0d3c824784ff121c03cc031f944bc7e139a8f1870ffd2845cc2dd76f6c4/absl_py-2.1.0-py3-none-any.whl", hash = "sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308", size = 133706 }, + { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811 }, ] [[package]] @@ -30,16 +30,16 @@ wheels = [ [[package]] name = "aiohappyeyeballs" -version = "2.4.4" +version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/55/e4373e888fdacb15563ef6fa9fa8c8252476ea071e96fb46defac9f18bf2/aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", size = 21977 } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/74/fbb6559de3607b3300b9be3cc64e97548d55678e44623db17820dbd20002/aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8", size = 14756 }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, ] [[package]] name = "aiohttp" -version = "3.11.11" +version = "3.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -51,49 +51,79 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/ed/f26db39d29cd3cb2f5a3374304c713fe5ab5a0e4c8ee25a0c45cc6adf844/aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e", size = 7669618 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994 } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/7d/ff2e314b8f9e0b1df833e2d4778eaf23eae6b8cc8f922495d110ddcbf9e1/aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8", size = 708550 }, - { url = "https://files.pythonhosted.org/packages/09/b8/aeb4975d5bba233d6f246941f5957a5ad4e3def8b0855a72742e391925f2/aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5", size = 468430 }, - { url = "https://files.pythonhosted.org/packages/9c/5b/5b620279b3df46e597008b09fa1e10027a39467387c2332657288e25811a/aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2", size = 455593 }, - { url = "https://files.pythonhosted.org/packages/d8/75/0cdf014b816867d86c0bc26f3d3e3f194198dbf33037890beed629cd4f8f/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43", size = 1584635 }, - { url = "https://files.pythonhosted.org/packages/df/2f/95b8f4e4dfeb57c1d9ad9fa911ede35a0249d75aa339edd2c2270dc539da/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f", size = 1632363 }, - { url = "https://files.pythonhosted.org/packages/39/cb/70cf69ea7c50f5b0021a84f4c59c3622b2b3b81695f48a2f0e42ef7eba6e/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d", size = 1668315 }, - { url = "https://files.pythonhosted.org/packages/2f/cc/3a3fc7a290eabc59839a7e15289cd48f33dd9337d06e301064e1e7fb26c5/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef", size = 1589546 }, - { url = "https://files.pythonhosted.org/packages/15/b4/0f7b0ed41ac6000e283e7332f0f608d734b675a8509763ca78e93714cfb0/aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438", size = 1544581 }, - { url = "https://files.pythonhosted.org/packages/58/b9/4d06470fd85c687b6b0e31935ef73dde6e31767c9576d617309a2206556f/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3", size = 1529256 }, - { url = "https://files.pythonhosted.org/packages/61/a2/6958b1b880fc017fd35f5dfb2c26a9a50c755b75fd9ae001dc2236a4fb79/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55", size = 1536592 }, - { url = "https://files.pythonhosted.org/packages/0f/dd/b974012a9551fd654f5bb95a6dd3f03d6e6472a17e1a8216dd42e9638d6c/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e", size = 1607446 }, - { url = "https://files.pythonhosted.org/packages/e0/d3/6c98fd87e638e51f074a3f2061e81fcb92123bcaf1439ac1b4a896446e40/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33", size = 1628809 }, - { url = "https://files.pythonhosted.org/packages/a8/2e/86e6f85cbca02be042c268c3d93e7f35977a0e127de56e319bdd1569eaa8/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c", size = 1564291 }, - { url = "https://files.pythonhosted.org/packages/0b/8d/1f4ef3503b767717f65e1f5178b0173ab03cba1a19997ebf7b052161189f/aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745", size = 416601 }, - { url = "https://files.pythonhosted.org/packages/ad/86/81cb83691b5ace3d9aa148dc42bacc3450d749fc88c5ec1973573c1c1779/aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9", size = 442007 }, + { url = "https://files.pythonhosted.org/packages/6d/34/939730e66b716b76046dedfe0842995842fa906ccc4964bba414ff69e429/aiohttp-3.13.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2372b15a5f62ed37789a6b383ff7344fc5b9f243999b0cd9b629d8bc5f5b4155", size = 736471 }, + { url = "https://files.pythonhosted.org/packages/fd/cf/dcbdf2df7f6ca72b0bb4c0b4509701f2d8942cf54e29ca197389c214c07f/aiohttp-3.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7f8659a48995edee7229522984bd1009c1213929c769c2daa80b40fe49a180c", size = 493985 }, + { url = "https://files.pythonhosted.org/packages/9d/87/71c8867e0a1d0882dcbc94af767784c3cb381c1c4db0943ab4aae4fed65e/aiohttp-3.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:939ced4a7add92296b0ad38892ce62b98c619288a081170695c6babe4f50e636", size = 489274 }, + { url = "https://files.pythonhosted.org/packages/38/0f/46c24e8dae237295eaadd113edd56dee96ef6462adf19b88592d44891dc5/aiohttp-3.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6315fb6977f1d0dd41a107c527fee2ed5ab0550b7d885bc15fee20ccb17891da", size = 1668171 }, + { url = "https://files.pythonhosted.org/packages/eb/c6/4cdfb4440d0e28483681a48f69841fa5e39366347d66ef808cbdadddb20e/aiohttp-3.13.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e7352512f763f760baaed2637055c49134fd1d35b37c2dedfac35bfe5cf8725", size = 1636036 }, + { url = "https://files.pythonhosted.org/packages/84/37/8708cf678628216fb678ab327a4e1711c576d6673998f4f43e86e9ae90dd/aiohttp-3.13.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e09a0a06348a2dd73e7213353c90d709502d9786219f69b731f6caa0efeb46f5", size = 1727975 }, + { url = "https://files.pythonhosted.org/packages/e6/2e/3ebfe12fdcb9b5f66e8a0a42dffcd7636844c8a018f261efb2419f68220b/aiohttp-3.13.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a09a6d073fb5789456545bdee2474d14395792faa0527887f2f4ec1a486a59d3", size = 1815823 }, + { url = "https://files.pythonhosted.org/packages/a1/4f/ca2ef819488cbb41844c6cf92ca6dd15b9441e6207c58e5ae0e0fc8d70ad/aiohttp-3.13.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b59d13c443f8e049d9e94099c7e412e34610f1f49be0f230ec656a10692a5802", size = 1669374 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/1fe2e1179a0d91ce09c99069684aab619bf2ccde9b20bd6ca44f8837203e/aiohttp-3.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20db2d67985d71ca033443a1ba2001c4b5693fe09b0e29f6d9358a99d4d62a8a", size = 1555315 }, + { url = "https://files.pythonhosted.org/packages/5a/2b/f3781899b81c45d7cbc7140cddb8a3481c195e7cbff8e36374759d2ab5a5/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:960c2fc686ba27b535f9fd2b52d87ecd7e4fd1cf877f6a5cba8afb5b4a8bd204", size = 1639140 }, + { url = "https://files.pythonhosted.org/packages/72/27/c37e85cd3ece6f6c772e549bd5a253d0c122557b25855fb274224811e4f2/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6c00dbcf5f0d88796151e264a8eab23de2997c9303dd7c0bf622e23b24d3ce22", size = 1645496 }, + { url = "https://files.pythonhosted.org/packages/66/20/3af1ab663151bd3780b123e907761cdb86ec2c4e44b2d9b195ebc91fbe37/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fed38a5edb7945f4d1bcabe2fcd05db4f6ec7e0e82560088b754f7e08d93772d", size = 1697625 }, + { url = "https://files.pythonhosted.org/packages/95/eb/ae5cab15efa365e13d56b31b0d085a62600298bf398a7986f8388f73b598/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b395bbca716c38bef3c764f187860e88c724b342c26275bc03e906142fc5964f", size = 1542025 }, + { url = "https://files.pythonhosted.org/packages/e9/2d/1683e8d67ec72d911397fe4e575688d2a9b8f6a6e03c8fdc9f3fd3d4c03f/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:204ffff2426c25dfda401ba08da85f9c59525cdc42bda26660463dd1cbcfec6f", size = 1714918 }, + { url = "https://files.pythonhosted.org/packages/99/a2/ffe8e0e1c57c5e542d47ffa1fcf95ef2b3ea573bf7c4d2ee877252431efc/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05c4dd3c48fb5f15db31f57eb35374cb0c09afdde532e7fb70a75aede0ed30f6", size = 1656113 }, + { url = "https://files.pythonhosted.org/packages/0d/42/d511aff5c3a2b06c09d7d214f508a4ad8ac7799817f7c3d23e7336b5e896/aiohttp-3.13.2-cp310-cp310-win32.whl", hash = "sha256:e574a7d61cf10351d734bcddabbe15ede0eaa8a02070d85446875dc11189a251", size = 432290 }, + { url = "https://files.pythonhosted.org/packages/8b/ea/1c2eb7098b5bad4532994f2b7a8228d27674035c9b3234fe02c37469ef14/aiohttp-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:364f55663085d658b8462a1c3f17b2b84a5c2e1ba858e1b79bff7b2e24ad1514", size = 455075 }, ] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, ] [[package]] name = "alembic" -version = "1.14.1" +version = "1.17.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, + { name = "tomli" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/09/f844822e4e847a3f0bd41797f93c4674cd4d2462a3f6c459aa528cdf786e/alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213", size = 1918219 } +sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554 }, +] + +[[package]] +name = "altair" +version = "4.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "entrypoints" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/bf/781b607da4c1a2a7211cd570bd7e22e0accd4deaf1074c32ac7344a09339/altair-4.2.2.tar.gz", hash = "sha256:39399a267c49b30d102c10411e67ab26374156a84b1aeb9fcd15140429ba49c5", size = 740430 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/62/47452306e84d4d2e67f9c559380aeb230f5e6ca84fafb428dd36b96a99ba/altair-4.2.2-py3-none-any.whl", hash = "sha256:8b45ebeaf8557f2d760c5c77b79f02ae12aee7c46c27c06014febab6f849bc87", size = 813630 }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/7e/ac0991d1745f7d755fc1cd381b3990a45b404b4d008fc75e2a983516fbfe/alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5", size = 233565 }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, ] [[package]] @@ -124,17 +154,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.8.0" +version = "4.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup" }, { name = "idna" }, - { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } +sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266 } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, + { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362 }, ] [[package]] @@ -157,57 +186,62 @@ wheels = [ [[package]] name = "argon2-cffi" -version = "23.1.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124 }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, ] [[package]] name = "argon2-cffi-bindings" -version = "21.2.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/e9/184b8ccce6683b0aa2fbb7ba5683ea4b9c5763f1356347f1312c32e3c66e/argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", size = 1779911 } +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/13/838ce2620025e9666aa8f686431f67a29052241692a3dd1ae9d3692a89d3/argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", size = 29658 }, - { url = "https://files.pythonhosted.org/packages/b3/02/f7f7bb6b6af6031edb11037639c697b912e1dea2db94d436e681aea2f495/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", size = 80583 }, - { url = "https://files.pythonhosted.org/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", size = 86168 }, - { url = "https://files.pythonhosted.org/packages/74/f6/4a34a37a98311ed73bb80efe422fed95f2ac25a4cacc5ae1d7ae6a144505/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", size = 82709 }, - { url = "https://files.pythonhosted.org/packages/74/2b/73d767bfdaab25484f7e7901379d5f8793cccbb86c6e0cbc4c1b96f63896/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", size = 83613 }, - { url = "https://files.pythonhosted.org/packages/4f/fd/37f86deef67ff57c76f137a67181949c2d408077e2e3dd70c6c42912c9bf/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", size = 84583 }, - { url = "https://files.pythonhosted.org/packages/6f/52/5a60085a3dae8fded8327a4f564223029f5f54b0cb0455a31131b5363a01/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", size = 88475 }, - { url = "https://files.pythonhosted.org/packages/8b/95/143cd64feb24a15fa4b189a3e1e7efbaeeb00f39a51e99b26fc62fbacabd/argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", size = 27698 }, - { url = "https://files.pythonhosted.org/packages/37/2c/e34e47c7dee97ba6f01a6203e0383e15b60fb85d78ac9a15cd066f6fe28b/argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", size = 30817 }, - { url = "https://files.pythonhosted.org/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, + { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549 }, + { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539 }, + { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467 }, + { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355 }, + { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187 }, ] [[package]] name = "arrow" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, - { name = "types-python-dateutil" }, + { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85", size = 131960 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419 }, + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, ] [[package]] name = "asttokens" -version = "3.0.0" +version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, ] [[package]] @@ -225,14 +259,14 @@ wheels = [ [[package]] name = "async-lru" -version = "2.0.4" +version = "2.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/e2/2b4651eff771f6fd900d233e175ddc5e2be502c7eb62c0c42f975c6d36cd/async-lru-2.0.4.tar.gz", hash = "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627", size = 10019 } +sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/9f/3c3503693386c4b0f245eaf5ca6198e3b28879ca0a40bde6b0e319793453/async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224", size = 6111 }, + { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069 }, ] [[package]] @@ -258,11 +292,11 @@ wheels = [ [[package]] name = "attrs" -version = "25.1.0" +version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, ] [[package]] @@ -298,11 +332,12 @@ dependencies = [ { name = "pytorch-lightning" }, { name = "requests" }, { name = "scipy" }, + { name = "sentence-transformers" }, { name = "sentencepiece" }, { name = "sqlmodel" }, { name = "tenacity" }, { name = "tensorflow-hub" }, - { name = "tensorflow-text" }, + { name = "tensorflow-text", marker = "(platform_machine == 'x86_64' and sys_platform == 'darwin') or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "textract" }, { name = "tiktoken" }, { name = "torch" }, @@ -356,15 +391,16 @@ requires-dist = [ { name = "pytorch-lightning", specifier = "==1.8.3.post1" }, { name = "requests", specifier = ">=2.32.3" }, { name = "scipy", specifier = "<1.14.1" }, + { name = "sentence-transformers", specifier = ">=2.2.0" }, { name = "sentencepiece", specifier = "==0.2.0" }, { name = "sqlmodel", specifier = "==0.0.22" }, { name = "tenacity", specifier = ">=9.0.0" }, { name = "tensorflow-hub", specifier = ">=0.16.1" }, - { name = "tensorflow-text", specifier = "==2.10.0" }, + { name = "tensorflow-text", marker = "(platform_machine == 'x86_64' and sys_platform == 'darwin') or sys_platform == 'linux' or sys_platform == 'win32'", specifier = "==2.10.0" }, { name = "textract", specifier = "==1.6.5" }, { name = "tiktoken", specifier = "==0.12.0" }, - { name = "torch", specifier = "==2.9.0" }, - { name = "torchtext", specifier = "==0.18.0" }, + { name = "torch", specifier = "==1.12.1" }, + { name = "torchtext", specifier = "==0.13.1" }, { name = "unidecode", specifier = "==1.3.8" }, { name = "uvicorn", specifier = ">=0.34.0" }, { name = "xmltodict", specifier = "==0.14.2" }, @@ -386,11 +422,20 @@ dev = [ [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, +] + +[[package]] +name = "backoff" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, ] [[package]] @@ -432,14 +477,14 @@ wheels = [ [[package]] name = "bleach" -version = "6.2.0" +version = "6.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/9a/0e33f5054c54d349ea62c277191c020c2d6ef1d65ab2cb1993f91ec846d1/bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f", size = 203083 } +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e", size = 163406 }, + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, ] [package.optional-dependencies] @@ -458,80 +503,80 @@ wheels = [ [[package]] name = "boto3" -version = "1.36.6" +version = "1.42.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/31/f6189fcb81156cd2e7f7616e4c95958a47e53c12253c5e86a9dcc1a529c1/boto3-1.36.6.tar.gz", hash = "sha256:b36feae061dc0793cf311468956a0a9e99215ce38bc99a1a4e55a5b105f16297", size = 110998 } +sdist = { url = "https://files.pythonhosted.org/packages/ef/4e/b1add2d248d7120ee59df1ca1781a0d5fabdb9e45a477ef50c7598908e80/boto3-1.42.3.tar.gz", hash = "sha256:f7fe002c39806d5efe9105f0d74e836f001230e9520b4502c752dd4951f60041", size = 112797 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/44/4b4d7579297708b84d846a223b6e3be93767d12a3ee997e162e2a3a371a5/boto3-1.36.6-py3-none-any.whl", hash = "sha256:6d473f0f340d02b4e9ad5b8e68786a09728101a8b950231b89ebdaf72b6dca21", size = 139166 }, + { url = "https://files.pythonhosted.org/packages/73/ac/81fb1f37bef7f29f7d0d4b71eb64c6425917cb230da437b42a2a58335ddf/boto3-1.42.3-py3-none-any.whl", hash = "sha256:163df55a774402fa940e95a152c308a33fb0f49cbcb5ec1725936e48635512f7", size = 140619 }, ] [[package]] name = "botocore" -version = "1.36.6" +version = "1.42.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/b6/bd1a28becf386a70ca41aa6b76b0d65d03aed81d39fac662d1f97754ffca/botocore-1.36.6.tar.gz", hash = "sha256:4864c53d638da191a34daf3ede3ff1371a3719d952cc0c6bd24ce2836a38dd77", size = 13479626 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/e1/8238fbbab5c45def1b4f75d486a95d1d838e8e21a348563d075a2b09f74b/botocore-1.42.3.tar.gz", hash = "sha256:6bad2e512ab85926bbfb391a9486bb3120f4be71419e2f70b556d99783dcb1ce", size = 14842884 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/ef/a0ac0d749c82fbaba28aa7b87a8ef5760de744e57dcbbfe3b99a0d65cf87/botocore-1.36.6-py3-none-any.whl", hash = "sha256:f77bbbb03fb420e260174650fb5c0cc142ec20a96967734eed2b0ef24334ef34", size = 13308005 }, + { url = "https://files.pythonhosted.org/packages/05/7f/69721ba9d89b4053ac243016428cdd2ea1a1f1c7217572a965aab9f57a20/botocore-1.42.3-py3-none-any.whl", hash = "sha256:fa174f53224ab2adc3a01bb3215b41d314df2f6578381a4a0051bd60d5c718d9", size = 14517527 }, ] [[package]] name = "cachetools" -version = "5.5.1" +version = "6.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/74/57df1ab0ce6bc5f6fa868e08de20df8ac58f9c44330c7671ad922d2bbeae/cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95", size = 28044 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/44/ca1675be2a83aeee1886ab745b28cda92093066590233cc501890eb8417a/cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6", size = 31571 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/4e/de4ff18bcf55857ba18d3a4bd48c8a9fde6bb0980c9d20b263f05387fd88/cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb", size = 9530 }, + { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503 }, ] [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.11.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, + { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438 }, ] [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser" }, + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283 }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504 }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811 }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402 }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217 }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079 }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475 }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829 }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211 }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184 }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790 }, ] [[package]] name = "cfgv" -version = "3.4.0" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, ] [[package]] @@ -545,36 +590,48 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, - { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, - { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, - { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, - { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, - { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, - { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, - { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, - { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, - { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, - { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, - { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, - { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, - { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, ] [[package]] name = "click" -version = "8.1.8" +version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "platform_system == 'Windows'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, ] [[package]] @@ -609,21 +666,21 @@ wheels = [ [[package]] name = "comm" -version = "0.2.2" +version = "0.2.3" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/a8/fb783cb0abe2b5fded9f55e5703015cdf1c9c85b3669087c538dd15a6a86/comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e", size = 6210 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180 }, + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, ] [[package]] name = "compressed-rtf" -version = "1.0.6" +version = "1.0.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/ac/abb196bb0b42a239d605fe97c314c3312374749013a07da4e6e0408f223c/compressed_rtf-1.0.6.tar.gz", hash = "sha256:c1c827f1d124d24608981a56e8b8691eb1f2a69a78ccad6440e7d92fde1781dd", size = 5800 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/0c/929a4e8ef9d7143f54d77dadb5f370cc7b98534b1bd6e1124d0abe8efb24/compressed_rtf-1.0.7.tar.gz", hash = "sha256:7c30859334839f3cdc7d10796af5b434bb326b9df7cb5a65e95a8eacb2951b0e", size = 8152 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/1d/62f5bf92e12335eb63517f42671ed78512d48bbc69e02a942dd7b90f03f0/compressed_rtf-1.0.7-py3-none-any.whl", hash = "sha256:b7904921d78c67a0a4b7fff9fb361a00ae2b447b6edca010ce321cd98fa0fcc0", size = 7968 }, +] [[package]] name = "conllu" @@ -636,26 +693,26 @@ wheels = [ [[package]] name = "contourpy" -version = "1.3.1" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/80937fe3efe0edacf67c9a20b955139a1a622730042c1ea991956f2704ad/contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab", size = 268466 }, - { url = "https://files.pythonhosted.org/packages/82/1d/e3eaebb4aa2d7311528c048350ca8e99cdacfafd99da87bc0a5f8d81f2c2/contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124", size = 253314 }, - { url = "https://files.pythonhosted.org/packages/de/f3/d796b22d1a2b587acc8100ba8c07fb7b5e17fde265a7bb05ab967f4c935a/contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1", size = 312003 }, - { url = "https://files.pythonhosted.org/packages/bf/f5/0e67902bc4394daee8daa39c81d4f00b50e063ee1a46cb3938cc65585d36/contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b", size = 351896 }, - { url = "https://files.pythonhosted.org/packages/1f/d6/e766395723f6256d45d6e67c13bb638dd1fa9dc10ef912dc7dd3dcfc19de/contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453", size = 320814 }, - { url = "https://files.pythonhosted.org/packages/a9/57/86c500d63b3e26e5b73a28b8291a67c5608d4aa87ebd17bd15bb33c178bc/contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3", size = 324969 }, - { url = "https://files.pythonhosted.org/packages/b8/62/bb146d1289d6b3450bccc4642e7f4413b92ebffd9bf2e91b0404323704a7/contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277", size = 1265162 }, - { url = "https://files.pythonhosted.org/packages/18/04/9f7d132ce49a212c8e767042cc80ae390f728060d2eea47058f55b9eff1c/contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595", size = 1324328 }, - { url = "https://files.pythonhosted.org/packages/46/23/196813901be3f97c83ababdab1382e13e0edc0bb4e7b49a7bff15fcf754e/contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697", size = 173861 }, - { url = "https://files.pythonhosted.org/packages/e0/82/c372be3fc000a3b2005061ca623a0d1ecd2eaafb10d9e883a2fc8566e951/contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e", size = 218566 }, - { url = "https://files.pythonhosted.org/packages/3e/4f/e56862e64b52b55b5ddcff4090085521fc228ceb09a88390a2b103dccd1b/contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6", size = 265605 }, - { url = "https://files.pythonhosted.org/packages/b0/2e/52bfeeaa4541889f23d8eadc6386b442ee2470bd3cff9baa67deb2dd5c57/contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750", size = 315040 }, - { url = "https://files.pythonhosted.org/packages/52/94/86bfae441707205634d80392e873295652fc313dfd93c233c52c4dc07874/contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53", size = 218221 }, + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551 }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399 }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061 }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956 }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872 }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027 }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641 }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075 }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534 }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188 }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681 }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101 }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599 }, ] [[package]] @@ -669,13 +726,13 @@ wheels = [ [[package]] name = "datasets" -version = "3.2.0" +version = "4.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp" }, { name = "dill" }, { name = "filelock" }, { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, { name = "huggingface-hub" }, { name = "multiprocess" }, { name = "numpy" }, @@ -687,9 +744,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/48/744286c044e2b942d4fa67f92816126522ad1f0675def0ea3264e6242005/datasets-3.2.0.tar.gz", hash = "sha256:9a6e1a356052866b5dbdd9c9eedb000bf3fc43d986e3584d9b028f4976937229", size = 558366 } +sdist = { url = "https://files.pythonhosted.org/packages/93/bf/0dae295d6d1ba0b1a200a9dd216838464b5bbd05da01407cb1330b377445/datasets-4.4.1.tar.gz", hash = "sha256:80322699aa8c0bbbdb7caa87906da689c3c2e29523cff698775c67f28fdab1fc", size = 585341 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/84/0df6c5981f5fc722381662ff8cfbdf8aad64bec875f75d80b55bfef394ce/datasets-3.2.0-py3-none-any.whl", hash = "sha256:f3d2ba2698b7284a4518019658596a6a8bc79f31e51516524249d6c59cf0fe2a", size = 480647 }, + { url = "https://files.pythonhosted.org/packages/3b/5e/6f8d874366788ad5d549e9ba258037d974dda6e004843be1bda794571701/datasets-4.4.1-py3-none-any.whl", hash = "sha256:c1163de5211e42546079ab355cc0250c7e6db16eb209ac5ac6252f801f596c44", size = 511591 }, ] [[package]] @@ -699,24 +756,24 @@ source = { git = "https://github.com/jedzill4/datetime_matcher#0e5793e8d1e3653f7 [[package]] name = "debugpy" -version = "1.8.12" +version = "1.8.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/25/c74e337134edf55c4dfc9af579eccb45af2393c40960e2795a94351e8140/debugpy-1.8.12.tar.gz", hash = "sha256:646530b04f45c830ceae8e491ca1c9320a2d2f0efea3141487c82130aba70dce", size = 1641122 } +sdist = { url = "https://files.pythonhosted.org/packages/15/ad/71e708ff4ca377c4230530d6a7aa7992592648c122a2cd2b321cf8b35a76/debugpy-1.8.17.tar.gz", hash = "sha256:fd723b47a8c08892b1a16b2c6239a8b96637c62a59b94bb5dab4bac592a58a8e", size = 1644129 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/19/dd58334c0a1ec07babf80bf29fb8daf1a7ca4c1a3bbe61548e40616ac087/debugpy-1.8.12-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:a2ba7ffe58efeae5b8fad1165357edfe01464f9aef25e814e891ec690e7dd82a", size = 2076091 }, - { url = "https://files.pythonhosted.org/packages/4c/37/bde1737da15f9617d11ab7b8d5267165f1b7dae116b2585a6643e89e1fa2/debugpy-1.8.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbbd4149c4fc5e7d508ece083e78c17442ee13b0e69bfa6bd63003e486770f45", size = 3560717 }, - { url = "https://files.pythonhosted.org/packages/d9/ca/bc67f5a36a7de072908bc9e1156c0f0b272a9a2224cf21540ab1ffd71a1f/debugpy-1.8.12-cp310-cp310-win32.whl", hash = "sha256:b202f591204023b3ce62ff9a47baa555dc00bb092219abf5caf0e3718ac20e7c", size = 5180672 }, - { url = "https://files.pythonhosted.org/packages/c1/b9/e899c0a80dfa674dbc992f36f2b1453cd1ee879143cdb455bc04fce999da/debugpy-1.8.12-cp310-cp310-win_amd64.whl", hash = "sha256:9649eced17a98ce816756ce50433b2dd85dfa7bc92ceb60579d68c053f98dff9", size = 5212702 }, - { url = "https://files.pythonhosted.org/packages/38/c4/5120ad36405c3008f451f94b8f92ef1805b1e516f6ff870f331ccb3c4cc0/debugpy-1.8.12-py2.py3-none-any.whl", hash = "sha256:274b6a2040349b5c9864e475284bce5bb062e63dce368a394b8cc865ae3b00c6", size = 5229490 }, + { url = "https://files.pythonhosted.org/packages/38/36/b57c6e818d909f6e59c0182252921cf435e0951126a97e11de37e72ab5e1/debugpy-1.8.17-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:c41d2ce8bbaddcc0009cc73f65318eedfa3dbc88a8298081deb05389f1ab5542", size = 2098021 }, + { url = "https://files.pythonhosted.org/packages/be/01/0363c7efdd1e9febd090bb13cee4fb1057215b157b2979a4ca5ccb678217/debugpy-1.8.17-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:1440fd514e1b815edd5861ca394786f90eb24960eb26d6f7200994333b1d79e3", size = 3087399 }, + { url = "https://files.pythonhosted.org/packages/79/bc/4a984729674aa9a84856650438b9665f9a1d5a748804ac6f37932ce0d4aa/debugpy-1.8.17-cp310-cp310-win32.whl", hash = "sha256:3a32c0af575749083d7492dc79f6ab69f21b2d2ad4cd977a958a07d5865316e4", size = 5230292 }, + { url = "https://files.pythonhosted.org/packages/5d/19/2b9b3092d0cf81a5aa10c86271999453030af354d1a5a7d6e34c574515d7/debugpy-1.8.17-cp310-cp310-win_amd64.whl", hash = "sha256:a3aad0537cf4d9c1996434be68c6c9a6d233ac6f76c2a482c7803295b4e4f99a", size = 5261885 }, + { url = "https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl", hash = "sha256:60c7dca6571efe660ccb7a9508d73ca14b8796c4ed484c2002abba714226cfef", size = 5283210 }, ] [[package]] name = "decorator" -version = "5.1.1" +version = "5.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, ] [[package]] @@ -730,23 +787,23 @@ wheels = [ [[package]] name = "deprecated" -version = "1.2.17" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/20/caa25c084ebad492360bf28ba5cb74f27b50fc6f3df965fd0add2b5b5993/deprecated-1.2.17.tar.gz", hash = "sha256:0114a10f0bbb750b90b2c2296c90cf7e9eaeb0abb5cf06c80de2c60138de0a82", size = 2928237 } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/90/89be9a665bf63d3baaba6f34d9a514655abb42be2cc129f31c96df2cef51/Deprecated-1.2.17-py2.py3-none-any.whl", hash = "sha256:69cdc0a751671183f569495e2efb14baee4344b0236342eec29f1fde25d61818", size = 9140 }, + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, ] [[package]] name = "dill" -version = "0.3.8" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668 }, ] [[package]] @@ -760,11 +817,20 @@ wheels = [ [[package]] name = "distlib" -version = "0.3.9" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, +] + +[[package]] +name = "distro" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, ] [[package]] @@ -778,11 +844,11 @@ wheels = [ [[package]] name = "dnspython" -version = "2.7.0" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, ] [[package]] @@ -793,9 +859,46 @@ sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf57 [[package]] name = "docx2txt" -version = "0.8" +version = "0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025 }, +] + +[[package]] +name = "dspy" +version = "3.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/7d/60ee3f2b16d9bfdfa72e8599470a2c1a5b759cb113c6fe1006be28359327/docx2txt-0.8.tar.gz", hash = "sha256:2c06d98d7cfe2d3947e5760a57d924e3ff07745b379c8737723922e7009236e5", size = 2814 } +dependencies = [ + { name = "anyio" }, + { name = "asyncer" }, + { name = "backoff" }, + { name = "cachetools" }, + { name = "cloudpickle" }, + { name = "diskcache" }, + { name = "gepa" }, + { name = "joblib" }, + { name = "json-repair" }, + { name = "litellm" }, + { name = "magicattr" }, + { name = "numpy" }, + { name = "openai" }, + { name = "optuna" }, + { name = "orjson" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "regex" }, + { name = "requests" }, + { name = "rich" }, + { name = "tenacity" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/18/0042d299cd5e85fdb381568f0cfcc7769122e8f70ea0a2d33e12fd63e705/dspy-3.0.4.tar.gz", hash = "sha256:cb4529df9a91353a16144d9d94ba6ff25f36fc5adfd921f127f4c49d0e309fb8", size = 236376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/52/56eed4828175f48f712a50a994293065afa7cc98cb112992a0b071179b6c/dspy-3.0.4-py3-none-any.whl", hash = "sha256:c0a88c7936f41f6f613ee6ca8cd92e63746ff2bd780e3896615ade7628eb6a6a", size = 285224 }, +] [[package]] name = "dspy" @@ -841,15 +944,24 @@ wheels = [ [[package]] name = "email-validator" -version = "2.2.0" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, +] + +[[package]] +name = "entrypoints" +version = "0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/8d/a7121ffe5f402dc015277d2d31eb82d2187334503a011c18f2e78ecbb9b2/entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", size = 13974 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 }, + { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294 }, ] [[package]] @@ -863,20 +975,23 @@ wheels = [ [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 }, ] [[package]] name = "executing" -version = "2.2.0" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, ] [[package]] @@ -909,16 +1024,17 @@ wheels = [ [[package]] name = "fastapi" -version = "0.115.7" +version = "0.123.9" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "annotated-doc" }, { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/f5/3f921e59f189e513adb9aef826e2841672d50a399fead4e69afdeb808ff4/fastapi-0.115.7.tar.gz", hash = "sha256:0f106da6c01d88a6786b3248fb4d7a940d071f6f488488898ad5d354b25ed015", size = 293177 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/01/c3fb48c0135d89586a03c3e2c5bc04540dda52079a1af5cac4a63598efb9/fastapi-0.123.9.tar.gz", hash = "sha256:ab33d672d8e1cc6e0b49777eb73c32ccf20761011f5ca16755889ab406fd1de0", size = 355616 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/7f/bbd4dcf0faf61bc68a01939256e2ed02d681e9334c1a3cef24d5f77aba9f/fastapi-0.115.7-py3-none-any.whl", hash = "sha256:eb6a8c8bf7f26009e8147111ff15b5177a0e19bb4a45bc3486ab14804539d21e", size = 94777 }, + { url = "https://files.pythonhosted.org/packages/db/15/a785e992a27620e022d0bc61b6c897ec14cff07c5ab7ff9f27651a21570b/fastapi-0.123.9-py3-none-any.whl", hash = "sha256:f54c69f23db14bd3dbcdfaf3fdce0483ca5f499512380c8e379a70cda30aa920", size = 111776 }, ] [package.optional-dependencies] @@ -933,30 +1049,104 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.7" +version = "0.0.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, + { name = "tomli" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/73/82a5831fbbf8ed75905bacf5b2d9d3dfd6f04d6968b29fe6f72a5ae9ceb1/fastapi_cli-0.0.7.tar.gz", hash = "sha256:02b3b65956f526412515907a0793c9094abd4bfb5457b389f645b0ea6ba3605e", size = 16753 } +sdist = { url = "https://files.pythonhosted.org/packages/99/75/9407a6b452be4c988feacec9c9d2f58d8f315162a6c7258d5a649d933ebe/fastapi_cli-0.0.16.tar.gz", hash = "sha256:e8a2a1ecf7a4e062e3b2eec63ae34387d1e142d4849181d936b23c4bdfe29073", size = 19447 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/e6/5daefc851b514ce2287d8f5d358ae4341089185f78f3217a69d0ce3a390c/fastapi_cli-0.0.7-py3-none-any.whl", hash = "sha256:d549368ff584b2804336c61f192d86ddea080c11255f375959627911944804f4", size = 10705 }, + { url = "https://files.pythonhosted.org/packages/55/43/678528c19318394320ee43757648d5e0a8070cf391b31f69d931e5c840d2/fastapi_cli-0.0.16-py3-none-any.whl", hash = "sha256:addcb6d130b5b9c91adbbf3f2947fe115991495fdb442fe3e51b5fc6327df9f4", size = 12312 }, ] [package.optional-dependencies] standard = [ + { name = "fastapi-cloud-cli" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[[package]] +name = "fastapi-cloud-cli" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastar" }, + { name = "httpx" }, + { name = "pydantic", extra = ["email"] }, + { name = "rich-toolkit" }, + { name = "rignore" }, + { name = "sentry-sdk" }, + { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] +sdist = { url = "https://files.pythonhosted.org/packages/c2/dd/e5890bb4ee63f9d8988660b755490e346cf5769aaa7f5f3ced9afb9f090a/fastapi_cloud_cli-0.6.0.tar.gz", hash = "sha256:2c333fff2e4b93b9efbec7896ce3ffa3e77ce4cf3d8cb14e35b4f823dfddac02", size = 30579 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/2f/5ba9b5faa75067e30ff48e3c454263ebc2d2301d5509cfefe12cf9fc8156/fastapi_cloud_cli-0.6.0-py3-none-any.whl", hash = "sha256:b654890b5302c90d2f347b123a35186096328838a526316c470b6005cabd4983", size = 23215 }, +] + +[[package]] +name = "fastar" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536 }, + { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235 }, + { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386 }, + { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955 }, + { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987 }, + { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900 }, + { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523 }, + { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268 }, + { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286 }, + { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921 }, + { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302 }, + { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141 }, + { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544 }, + { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701 }, + { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544 }, + { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020 }, + { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735 }, + { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779 }, + { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755 }, + { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732 }, + { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571 }, + { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440 }, + { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424 }, + { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675 }, + { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098 }, + { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397 }, +] [[package]] name = "fastjsonschema" -version = "2.21.1" +version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/50/4b769ce1ac4071a1ef6d86b1a3fb56cdc3a37615e8c5519e1af96cdac366/fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", size = 373939 } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, +] + +[[package]] +name = "fastuuid" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232 } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 }, + { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760 }, + { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748 }, + { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537 }, + { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994 }, + { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003 }, + { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583 }, + { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955 }, + { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763 }, + { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613 }, + { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045 }, + { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122 }, ] [[package]] @@ -980,21 +1170,24 @@ wheels = [ [[package]] name = "filelock" -version = "3.17.0" +version = "3.20.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 } +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922 } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, + { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 }, ] [[package]] name = "fire" -version = "0.7.0" +version = "0.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "termcolor" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/b6/82c7e601d6d3c3278c40b7bd35e17e82aa227f050aa9f66cb7b7fce29471/fire-0.7.0.tar.gz", hash = "sha256:961550f07936eaf65ad1dc8360f2b2bf8408fad46abbfa4d2a3794f8d2a95cdf", size = 87189 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945 }, +] [[package]] name = "flair" @@ -1035,28 +1228,28 @@ wheels = [ [[package]] name = "flatbuffers" -version = "25.1.24" +version = "25.9.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/64/20/c380c311843318b577650286b2c7eaaac3a011fb982df0050bdbd7e453c5/flatbuffers-25.1.24.tar.gz", hash = "sha256:e0f7b7d806c0abdf166275492663130af40c11f89445045fbef0aa3c9a8643ad", size = 22155 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/1f/3ee70b0a55137442038f2a33469cc5fddd7e0ad2abf83d7497c18a2b6923/flatbuffers-25.9.23.tar.gz", hash = "sha256:676f9fa62750bb50cf531b42a0a2a118ad8f7f797a511eda12881c016f093b12", size = 22067 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/e2/b066e6e02d67bf5261a6d7539648c6da3365cc9eff3eb6d82009595d84d9/flatbuffers-25.1.24-py2.py3-none-any.whl", hash = "sha256:1abfebaf4083117225d0723087ea909896a34e3fec933beedb490d595ba24145", size = 30955 }, + { url = "https://files.pythonhosted.org/packages/ee/1b/00a78aa2e8fbd63f9af08c9c19e6deb3d5d66b4dda677a0f61654680ee89/flatbuffers-25.9.23-py2.py3-none-any.whl", hash = "sha256:255538574d6cb6d0a79a17ec8bc0d30985913b87513a01cce8bcdb6b4c44d0e2", size = 30869 }, ] [[package]] name = "fonttools" -version = "4.55.6" +version = "4.61.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2e/0b11e907b90665253dbad425479e874e38a9e81ced397a4e3312b9116935/fonttools-4.55.6.tar.gz", hash = "sha256:1beb4647a0df5ceaea48015656525eb8081af226fe96554089fd3b274d239ef0", size = 3500677 } +sdist = { url = "https://files.pythonhosted.org/packages/33/f9/0e84d593c0e12244150280a630999835a64f2852276161b62a0f98318de0/fonttools-4.61.0.tar.gz", hash = "sha256:ec520a1f0c7758d7a858a00f090c1745f6cde6a7c5e76fb70ea4044a15f712e7", size = 3561884 } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/05/8c1a6ab8c443525c1cedee94d0371ec45cbcd11e4b01328ff10dcc483134/fonttools-4.55.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:57d55fc965e5dd20c8a60d880e0f43bafb506be87af0b650bdc42591e41e0d0d", size = 2774906 }, - { url = "https://files.pythonhosted.org/packages/cd/53/de15cea829b49d50a0d0942d75bc71ca680536265abed03ce873ae71787b/fonttools-4.55.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:127999618afe3a2490fad54bab0650c5fbeab1f8109bdc0205f6ad34306deb8b", size = 2303345 }, - { url = "https://files.pythonhosted.org/packages/d2/23/90159149cc907ea2da0ca7b7baf5ad783c902de219ecb28bca3f789b82c3/fonttools-4.55.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3226d40cb92787e09dcc3730f54b3779dfe56bdfea624e263685ba17a6faac4", size = 4584790 }, - { url = "https://files.pythonhosted.org/packages/26/db/8d33a4575efe7ecd0487d4a53369d086ab7d879069e4c62d3687dec53941/fonttools-4.55.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e82772f70b84e17aa36e9f236feb2a4f73cb686ec1e162557a36cf759d1acd58", size = 4627464 }, - { url = "https://files.pythonhosted.org/packages/a3/c9/e90342b5eebce21ba2b04ce879c66e0316a5faaa7337498dfb5032953055/fonttools-4.55.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a632f85bd73e002b771bcbcdc512038fa5d2e09bb18c03a22fb8d400ea492ddf", size = 4581741 }, - { url = "https://files.pythonhosted.org/packages/e1/98/f4297b65849f15f6b49349a1ffc21e93d1859fa3579d0d5cb1590317060c/fonttools-4.55.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:791e0cf862cdd3a252df395f1bb5f65e3a760f1da3c7ce184d0f7998c266614d", size = 4751195 }, - { url = "https://files.pythonhosted.org/packages/63/73/9dcf4040edbdc5e33cc23d2b7d6a6d4bb50605efd5686d6b782815f4a818/fonttools-4.55.6-cp310-cp310-win32.whl", hash = "sha256:94f7f2c5c5f3a6422e954ecb6d37cc363e27d6f94050a7ed3f79f12157af6bb2", size = 2178422 }, - { url = "https://files.pythonhosted.org/packages/45/f0/ec0ce63f910db60a566201a550c06205595c10c980f6c74885f53cdf512b/fonttools-4.55.6-cp310-cp310-win_amd64.whl", hash = "sha256:2d15e02b93a46982a8513a208e8f89148bca8297640527365625be56151687d0", size = 2222886 }, - { url = "https://files.pythonhosted.org/packages/1e/6a/6afc55d75036b8d3fe5ceaea2e8da2c04e8f3b298325de73a35f098cb9a8/fonttools-4.55.6-py3-none-any.whl", hash = "sha256:d20ab5a78d0536c26628eaadba661e7ae2427b1e5c748a0a510a44d914e1b155", size = 1112524 }, + { url = "https://files.pythonhosted.org/packages/29/f3/91bba2721fb173fc68e09d15b6ccf3ad4f83d127fbff579be7e5984888a6/fonttools-4.61.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dc25a4a9c1225653e4431a9413d0381b1c62317b0f543bdcec24e1991f612f33", size = 2850151 }, + { url = "https://files.pythonhosted.org/packages/f5/8c/a1691dec01038ac7e7bb3ab83300dcc5087b11d8f48640928c02a873eb92/fonttools-4.61.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b493c32d2555e9944ec1b911ea649ff8f01a649ad9cba6c118d6798e932b3f0", size = 2389769 }, + { url = "https://files.pythonhosted.org/packages/2d/dd/5bb369a44319d92ba25612511eb8ed2a6fa75239979e0388907525626902/fonttools-4.61.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad751319dc532a79bdf628b8439af167181b4210a0cd28a8935ca615d9fdd727", size = 4893189 }, + { url = "https://files.pythonhosted.org/packages/5e/02/51373fa8846bd22bb54e5efb30a824b417b058083f775a194a432f21a45f/fonttools-4.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2de14557d113faa5fb519f7f29c3abe4d69c17fe6a5a2595cc8cda7338029219", size = 4854415 }, + { url = "https://files.pythonhosted.org/packages/8b/64/9cdbbb804577a7e6191448851c57e6a36eb02aa4bf6a9668b528c968e44e/fonttools-4.61.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:59587bbe455dbdf75354a9dbca1697a35a8903e01fab4248d6b98a17032cee52", size = 4870927 }, + { url = "https://files.pythonhosted.org/packages/92/68/e40b22919dc96dc30a70b58fec609ab85112de950bdecfadf8dd478c5a88/fonttools-4.61.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:46cb3d9279f758ac0cf671dc3482da877104b65682679f01b246515db03dbb72", size = 4988674 }, + { url = "https://files.pythonhosted.org/packages/9b/5c/e857349ce8aedb2451b9448282e86544b2b7f1c8b10ea0fe49b7cb369b72/fonttools-4.61.0-cp310-cp310-win32.whl", hash = "sha256:58b4f1b78dfbfe855bb8a6801b31b8cdcca0e2847ec769ad8e0b0b692832dd3b", size = 1497663 }, + { url = "https://files.pythonhosted.org/packages/f9/0c/62961d5fe6f764d6cbc387ef2c001f5f610808c7aded837409836c0b3e7c/fonttools-4.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:68704a8bbe0b61976262b255e90cde593dc0fe3676542d9b4d846bad2a890a76", size = 1546143 }, + { url = "https://files.pythonhosted.org/packages/0c/14/634f7daea5ffe6a5f7a0322ba8e1a0e23c9257b80aa91458107896d1dfc7/fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635", size = 1144485 }, ] [[package]] @@ -1070,35 +1263,36 @@ wheels = [ [[package]] name = "frozenlist" -version = "1.5.0" +version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/79/29d44c4af36b2b240725dce566b20f63f9b36ef267aaaa64ee7466f4f2f8/frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a", size = 94451 }, - { url = "https://files.pythonhosted.org/packages/47/47/0c999aeace6ead8a44441b4f4173e2261b18219e4ad1fe9a479871ca02fc/frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb", size = 54301 }, - { url = "https://files.pythonhosted.org/packages/8d/60/107a38c1e54176d12e06e9d4b5d755b677d71d1219217cee063911b1384f/frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec", size = 52213 }, - { url = "https://files.pythonhosted.org/packages/17/62/594a6829ac5679c25755362a9dc93486a8a45241394564309641425d3ff6/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5", size = 240946 }, - { url = "https://files.pythonhosted.org/packages/7e/75/6c8419d8f92c80dd0ee3f63bdde2702ce6398b0ac8410ff459f9b6f2f9cb/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76", size = 264608 }, - { url = "https://files.pythonhosted.org/packages/88/3e/82a6f0b84bc6fb7e0be240e52863c6d4ab6098cd62e4f5b972cd31e002e8/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17", size = 261361 }, - { url = "https://files.pythonhosted.org/packages/fd/85/14e5f9ccac1b64ff2f10c927b3ffdf88772aea875882406f9ba0cec8ad84/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba", size = 231649 }, - { url = "https://files.pythonhosted.org/packages/ee/59/928322800306f6529d1852323014ee9008551e9bb027cc38d276cbc0b0e7/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d", size = 241853 }, - { url = "https://files.pythonhosted.org/packages/7d/bd/e01fa4f146a6f6c18c5d34cab8abdc4013774a26c4ff851128cd1bd3008e/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2", size = 243652 }, - { url = "https://files.pythonhosted.org/packages/a5/bd/e4771fd18a8ec6757033f0fa903e447aecc3fbba54e3630397b61596acf0/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f", size = 241734 }, - { url = "https://files.pythonhosted.org/packages/21/13/c83821fa5544af4f60c5d3a65d054af3213c26b14d3f5f48e43e5fb48556/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c", size = 260959 }, - { url = "https://files.pythonhosted.org/packages/71/f3/1f91c9a9bf7ed0e8edcf52698d23f3c211d8d00291a53c9f115ceb977ab1/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab", size = 262706 }, - { url = "https://files.pythonhosted.org/packages/4c/22/4a256fdf5d9bcb3ae32622c796ee5ff9451b3a13a68cfe3f68e2c95588ce/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5", size = 250401 }, - { url = "https://files.pythonhosted.org/packages/af/89/c48ebe1f7991bd2be6d5f4ed202d94960c01b3017a03d6954dd5fa9ea1e8/frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb", size = 45498 }, - { url = "https://files.pythonhosted.org/packages/28/2f/cc27d5f43e023d21fe5c19538e08894db3d7e081cbf582ad5ed366c24446/frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4", size = 51622 }, - { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230 }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621 }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889 }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464 }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649 }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188 }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748 }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351 }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767 }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887 }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785 }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312 }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650 }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659 }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837 }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, ] [[package]] name = "fsspec" -version = "2024.9.0" +version = "2025.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/7c/12b0943011daaaa9c35c2a2e22e5eb929ac90002f08f1259d69aedad84de/fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8", size = 286206 } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/a0/6aaea0c2fbea2f89bfd5db25fb1e3481896a423002ebe4e55288907a97a3/fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b", size = 179253 }, + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966 }, ] [package.optional-dependencies] @@ -1194,16 +1388,21 @@ wheels = [ [[package]] name = "google-auth" -version = "2.38.0" +version = "2.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/eb/d504ba1daf190af6b204a9d4714d457462b486043744901a6eeea711f913/google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4", size = 270866 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/47/603554949a37bca5b7f894d51896a9c534b9eab808e2520a748e081669d0/google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a", size = 210770 }, + { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114 }, +] + +[package.optional-dependencies] +requests = [ + { name = "requests" }, ] [[package]] @@ -1267,11 +1466,11 @@ wheels = [ [[package]] name = "google-genai" -version = "1.51.0" +version = "1.53.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "google-auth" }, + { name = "google-auth", extra = ["requests"] }, { name = "httpx" }, { name = "pydantic" }, { name = "requests" }, @@ -1279,9 +1478,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/1c/29245699c7c274ed5709b33b6a5192af2d57da5da3d2f189f222d1895336/google_genai-1.51.0.tar.gz", hash = "sha256:596c1ec964b70fec17a6ccfe6ee4edede31022584e8b1d33371d93037c4001b1", size = 258060 } +sdist = { url = "https://files.pythonhosted.org/packages/de/b3/36fbfde2e21e6d3bc67780b61da33632f495ab1be08076cf0a16af74098f/google_genai-1.53.0.tar.gz", hash = "sha256:938a26d22f3fd32c6eeeb4276ef204ef82884e63af9842ce3eac05ceb39cbd8d", size = 260102 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/28/0185dcda66f1994171067cfdb0e44a166450239d5b11b3a8a281dd2da459/google_genai-1.51.0-py3-none-any.whl", hash = "sha256:bfb7d0c6ba48ba9bda539f0d5e69dad827d8735a8b1e4703bafa0a2945d293e1", size = 260483 }, + { url = "https://files.pythonhosted.org/packages/40/f2/97fefdd1ad1f3428321bac819ae7a83ccc59f6439616054736b7819fa56c/google_genai-1.53.0-py3-none-any.whl", hash = "sha256:65a3f99e5c03c372d872cda7419f5940e723374bb12a2f3ffd5e3e56e8eb2094", size = 262015 }, ] [[package]] @@ -1322,89 +1521,105 @@ wheels = [ [[package]] name = "greenlet" -version = "3.1.1" +version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, - { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, - { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, - { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, - { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, - { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, - { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, - { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, - { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, + { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658 }, + { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810 }, + { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248 }, + { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403 }, + { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910 }, + { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206 }, + { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359 }, + { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740 }, ] [[package]] name = "grpcio" -version = "1.70.0" +version = "1.67.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/e1/4b21b5017c33f3600dcc32b802bb48fe44a4d36d6c066f52650c7c2690fa/grpcio-1.70.0.tar.gz", hash = "sha256:8d1584a68d5922330025881e63a6c1b54cc8117291d382e4fa69339b6d914c56", size = 12788932 } +sdist = { url = "https://files.pythonhosted.org/packages/20/53/d9282a66a5db45981499190b77790570617a604a38f3d103d0400974aeb5/grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732", size = 12580022 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/e9/f72408bac1f7b05b25e4df569b02d6b200c8e7857193aa9f1df7a3744add/grpcio-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:95469d1977429f45fe7df441f586521361e235982a0b39e33841549143ae2851", size = 5229736 }, - { url = "https://files.pythonhosted.org/packages/b3/17/e65139ea76dac7bcd8a3f17cbd37e3d1a070c44db3098d0be5e14c5bd6a1/grpcio-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ed9718f17fbdb472e33b869c77a16d0b55e166b100ec57b016dc7de9c8d236bf", size = 11432751 }, - { url = "https://files.pythonhosted.org/packages/a0/12/42de6082b4ab14a59d30b2fc7786882fdaa75813a4a4f3d4a8c4acd6ed59/grpcio-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:374d014f29f9dfdb40510b041792e0e2828a1389281eb590df066e1cc2b404e5", size = 5711439 }, - { url = "https://files.pythonhosted.org/packages/34/f8/b5a19524d273cbd119274a387bb72d6fbb74578e13927a473bc34369f079/grpcio-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2af68a6f5c8f78d56c145161544ad0febbd7479524a59c16b3e25053f39c87f", size = 6330777 }, - { url = "https://files.pythonhosted.org/packages/1a/67/3d6c0ad786238aac7fa93b79246fc452978fbfe9e5f86f70da8e8a2797d0/grpcio-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7df14b2dcd1102a2ec32f621cc9fab6695effef516efbc6b063ad749867295", size = 5944639 }, - { url = "https://files.pythonhosted.org/packages/76/0d/d9f7cbc41c2743cf18236a29b6a582f41bd65572a7144d92b80bc1e68479/grpcio-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c78b339869f4dbf89881e0b6fbf376313e4f845a42840a7bdf42ee6caed4b11f", size = 6643543 }, - { url = "https://files.pythonhosted.org/packages/fc/24/bdd7e606b3400c14330e33a4698fa3a49e38a28c9e0a831441adbd3380d2/grpcio-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58ad9ba575b39edef71f4798fdb5c7b6d02ad36d47949cd381d4392a5c9cbcd3", size = 6199897 }, - { url = "https://files.pythonhosted.org/packages/d1/33/8132eb370087960c82d01b89faeb28f3e58f5619ffe19889f57c58a19c18/grpcio-1.70.0-cp310-cp310-win32.whl", hash = "sha256:2b0d02e4b25a5c1f9b6c7745d4fa06efc9fd6a611af0fb38d3ba956786b95199", size = 3617513 }, - { url = "https://files.pythonhosted.org/packages/99/bc/0fce5cfc0ca969df66f5dca6cf8d2258abb88146bf9ab89d8cf48e970137/grpcio-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:0de706c0a5bb9d841e353f6343a9defc9fc35ec61d6eb6111802f3aa9fef29e1", size = 4303342 }, + { url = "https://files.pythonhosted.org/packages/4e/cd/f6ca5c49aa0ae7bc6d0757f7dae6f789569e9490a635eaabe02bc02de7dc/grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f", size = 5112450 }, + { url = "https://files.pythonhosted.org/packages/d4/f0/d9bbb4a83cbee22f738ee7a74aa41e09ccfb2dcea2cc30ebe8dab5b21771/grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d", size = 10937518 }, + { url = "https://files.pythonhosted.org/packages/5b/17/0c5dbae3af548eb76669887642b5f24b232b021afe77eb42e22bc8951d9c/grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f", size = 5633610 }, + { url = "https://files.pythonhosted.org/packages/17/48/e000614e00153d7b2760dcd9526b95d72f5cfe473b988e78f0ff3b472f6c/grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0", size = 6240678 }, + { url = "https://files.pythonhosted.org/packages/64/19/a16762a70eeb8ddfe43283ce434d1499c1c409ceec0c646f783883084478/grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa", size = 5884528 }, + { url = "https://files.pythonhosted.org/packages/6b/dc/bd016aa3684914acd2c0c7fa4953b2a11583c2b844f3d7bae91fa9b98fbb/grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292", size = 6583680 }, + { url = "https://files.pythonhosted.org/packages/1a/93/1441cb14c874f11aa798a816d582f9da82194b6677f0f134ea53d2d5dbeb/grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311", size = 6162967 }, + { url = "https://files.pythonhosted.org/packages/29/e9/9295090380fb4339b7e935b9d005fa9936dd573a22d147c9e5bb2df1b8d4/grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed", size = 3616336 }, + { url = "https://files.pythonhosted.org/packages/ce/de/7c783b8cb8f02c667ca075c49680c4aeb8b054bc69784bcb3e7c1bbf4985/grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e", size = 4352071 }, ] [[package]] name = "h11" -version = "0.14.0" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] [[package]] name = "h5py" -version = "3.12.1" +version = "3.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/0c/5c2b0a88158682aeafb10c1c2b735df5bc31f165bfe192f2ee9f2a23b5f1/h5py-3.12.1.tar.gz", hash = "sha256:326d70b53d31baa61f00b8aa5f95c2fcb9621a3ee8365d770c551a13dbbcbfdf", size = 411457 } +sdist = { url = "https://files.pythonhosted.org/packages/4d/6a/0d79de0b025aa85dc8864de8e97659c94cf3d23148394a954dc5ca52f8c8/h5py-3.15.1.tar.gz", hash = "sha256:c86e3ed45c4473564de55aa83b6fc9e5ead86578773dfbd93047380042e26b69", size = 426236 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/30/8fa61698b438dd751fa46a359792e801191dadab560d0a5f1c709443ef8e/h5py-3.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67e59f6c2f19a32973a40f43d9a088ae324fe228c8366e25ebc57ceebf093a6b", size = 3414477 }, + { url = "https://files.pythonhosted.org/packages/16/16/db2f63302937337c4e9e51d97a5984b769bdb7488e3d37632a6ac297f8ef/h5py-3.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e2f471688402c3404fa4e13466e373e622fd4b74b47b56cfdff7cc688209422", size = 2850298 }, + { url = "https://files.pythonhosted.org/packages/fc/2e/f1bb7de9b05112bfd14d5206090f0f92f1e75bbb412fbec5d4653c3d44dd/h5py-3.15.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c45802bcb711e128a6839cb6c01e9ac648dc55df045c9542a675c771f15c8d5", size = 4523605 }, + { url = "https://files.pythonhosted.org/packages/05/8a/63f4b08f3628171ce8da1a04681a65ee7ac338fde3cb3e9e3c9f7818e4da/h5py-3.15.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64ce3f6470adb87c06e3a8dd1b90e973699f1759ad79bfa70c230939bff356c9", size = 4735346 }, + { url = "https://files.pythonhosted.org/packages/74/48/f16d12d9de22277605bcc11c0dcab5e35f06a54be4798faa2636b5d44b3c/h5py-3.15.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4411c1867b9899a25e983fff56d820a66f52ac326bbe10c7cdf7d832c9dcd883", size = 4175305 }, + { url = "https://files.pythonhosted.org/packages/d6/2f/47cdbff65b2ce53c27458c6df63a232d7bb1644b97df37b2342442342c84/h5py-3.15.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2cbc4104d3d4aca9d6db8c0c694555e255805bfeacf9eb1349bda871e26cacbe", size = 4653602 }, + { url = "https://files.pythonhosted.org/packages/c3/28/dc08de359c2f43a67baa529cb70d7f9599848750031975eed92d6ae78e1d/h5py-3.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:01f55111ca516f5568ae7a7fc8247dfce607de331b4467ee8a9a6ed14e5422c7", size = 2873601 }, +] + +[[package]] +name = "hf-xet" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020 } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/7d/b21045fbb004ad8bb6fb3be4e6ca903841722706f7130b9bba31ef2f88e3/h5py-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f0f1a382cbf494679c07b4371f90c70391dedb027d517ac94fa2c05299dacda", size = 3402133 }, - { url = "https://files.pythonhosted.org/packages/29/a7/3c2a33fba1da64a0846744726fd067a92fb8abb887875a0dd8e3bac8b45d/h5py-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb65f619dfbdd15e662423e8d257780f9a66677eae5b4b3fc9dca70b5fd2d2a3", size = 2866436 }, - { url = "https://files.pythonhosted.org/packages/1e/d0/4bf67c3937a2437c20844165766ddd1a1817ae6b9544c3743050d8e0f403/h5py-3.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b15d8dbd912c97541312c0e07438864d27dbca857c5ad634de68110c6beb1c2", size = 5168596 }, - { url = "https://files.pythonhosted.org/packages/85/bc/e76f4b2096e0859225f5441d1b7f5e2041fffa19fc2c16756c67078417aa/h5py-3.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59685fe40d8c1fbbee088c88cd4da415a2f8bee5c270337dc5a1c4aa634e3307", size = 5341537 }, - { url = "https://files.pythonhosted.org/packages/99/bd/fb8ed45308bb97e04c02bd7aed324ba11e6a4bf9ed73967ca2a168e9cf92/h5py-3.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:577d618d6b6dea3da07d13cc903ef9634cde5596b13e832476dd861aaf651f3e", size = 2990575 }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099 }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178 }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214 }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054 }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812 }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920 }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 }, ] [[package]] name = "httpcore" -version = "1.0.7" +version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, ] [[package]] name = "httptools" -version = "0.6.4" +version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780 }, - { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297 }, - { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130 }, - { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148 }, - { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949 }, - { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591 }, - { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344 }, + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531 }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408 }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889 }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460 }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267 }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429 }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173 }, ] [[package]] @@ -1424,38 +1639,39 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.27.1" +version = "0.36.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, { name = "packaging" }, { name = "pyyaml" }, { name = "requests" }, { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/d2/d6976de7542792fc077b498d64af64882b6d8bb40679284ec0bff77d5929/huggingface_hub-0.27.1.tar.gz", hash = "sha256:c004463ca870283909d715d20f066ebd6968c2207dae9393fdffb3c1d4d8f98b", size = 379407 } +sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/3f/50f6b25fafdcfb1c089187a328c95081abf882309afd86f4053951507cd1/huggingface_hub-0.27.1-py3-none-any.whl", hash = "sha256:1c5155ca7d60b60c2e2fc38cbb3ffb7f7c3adf48f824015b219af9061771daec", size = 450658 }, + { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094 }, ] [[package]] name = "identify" -version = "2.6.6" +version = "2.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/bf/c68c46601bacd4c6fb4dd751a42b6e7087240eaabc6487f2ef7a48e0e8fc/identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251", size = 99217 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311 } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/a1/68a395c17eeefb04917034bd0a1bfa765e7654fa150cca473d669aa3afb5/identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881", size = 99083 }, + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183 }, ] [[package]] name = "idna" -version = "3.10" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, ] [[package]] @@ -1493,7 +1709,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/50/fb/396d568039d213446 [[package]] name = "ipykernel" -version = "6.29.5" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "appnope", marker = "platform_system == 'Darwin'" }, @@ -1510,14 +1726,14 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/67594cb0c7055dc50814b21731c22a601101ea3b1b50a9a1b090e11f5d0f/ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215", size = 163367 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/5c/368ae6c01c7628438358e6d337c19b05425727fbb221d2a3c4303c372f42/ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", size = 117173 }, + { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968 }, ] [[package]] name = "ipython" -version = "8.31.0" +version = "8.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1532,14 +1748,14 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/01/35/6f90fdddff7a08b7b715fccbd2427b5212c9525cd043d26fdc45bee0708d/ipython-8.31.0.tar.gz", hash = "sha256:b6a2274606bec6166405ff05e54932ed6e5cfecaca1fc05f2cacde7bb074d70b", size = 5501011 } +sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/60/d0feb6b6d9fe4ab89fe8fe5b47cbf6cd936bfd9f1e7ffa9d0015425aeed6/ipython-8.31.0-py3-none-any.whl", hash = "sha256:46ec58f8d3d076a61d128fe517a51eb730e3aaf0c184ea8c17d16e366660c6a6", size = 821583 }, + { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864 }, ] [[package]] name = "ipywidgets" -version = "8.1.5" +version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "comm" }, @@ -1548,9 +1764,9 @@ dependencies = [ { name = "traitlets" }, { name = "widgetsnbextension" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/4c/dab2a281b07596a5fc220d49827fe6c794c66f1493d7a74f1df0640f2cc5/ipywidgets-8.1.5.tar.gz", hash = "sha256:870e43b1a35656a80c18c9503bbf2d16802db1cb487eec6fab27d683381dde17", size = 116723 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/2d/9c0b76f2f9cc0ebede1b9371b6f317243028ed60b90705863d493bae622e/ipywidgets-8.1.5-py3-none-any.whl", hash = "sha256:3290f526f87ae6e77655555baba4f36681c555b8bdbbff430b70e52c34c86245", size = 139767 }, + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, ] [[package]] @@ -1579,14 +1795,42 @@ wheels = [ [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jiter" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, + { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652 }, + { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829 }, + { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568 }, + { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052 }, + { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585 }, + { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541 }, + { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423 }, + { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958 }, + { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084 }, + { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054 }, + { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368 }, + { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847 }, + { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144 }, + { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877 }, + { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419 }, + { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212 }, + { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974 }, + { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233 }, + { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537 }, + { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110 }, ] [[package]] @@ -1641,11 +1885,20 @@ wheels = [ [[package]] name = "joblib" -version = "1.4.2" +version = "1.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/5d/447af5ea094b9e4c4054f82e223ada074c552335b9b4b2d14bd9b35a67c4/joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55", size = 331077 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241", size = 308396 }, +] + +[[package]] +name = "json-repair" +version = "0.54.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/05/9fbcd5ffab9c41455e7d80af65a90876718b8ea2fb4525e187ab11836dd4/json_repair-0.54.2.tar.gz", hash = "sha256:4b6b62ce17f1a505b220fa4aadba1fc37dc9c221544f158471efe3775620bad6", size = 38575 } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, + { url = "https://files.pythonhosted.org/packages/53/3a/1b4df9adcd69fee9c9e4b439c13e8c866f2fae520054aede7030b2278be9/json_repair-0.54.2-py3-none-any.whl", hash = "sha256:be51cce5dca97e0c24ebdf61a1ede2449a8a7666012de99467bb7b0afb35179b", size = 29322 }, ] [[package]] @@ -1659,11 +1912,11 @@ wheels = [ [[package]] name = "json5" -version = "0.10.0" +version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/3d/bbe62f3d0c05a689c711cff57b2e3ac3d3e526380adb7c781989f075115c/json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559", size = 48202 } +sdist = { url = "https://files.pythonhosted.org/packages/12/ae/929aee9619e9eba9015207a9d2c1c54db18311da7eb4dcf6d41ad6f0eb67/json5-0.12.1.tar.gz", hash = "sha256:b2743e77b3242f8d03c143dd975a6ec7c52e2f2afe76ed934e53503dd4ad4990", size = 52191 } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/42/797895b952b682c3dafe23b1834507ee7f02f4d6299b65aaa61425763278/json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa", size = 34049 }, + { url = "https://files.pythonhosted.org/packages/85/e2/05328bd2621be49a6fed9e3030b1e51a2d04537d3f816d211b9cc53c5262/json5-0.12.1-py3-none-any.whl", hash = "sha256:d9c9b3bc34a5f54d43c35e11ef7cb87d8bdd098c6ace87117a7b7e83e705c1d5", size = 36119 }, ] [[package]] @@ -1689,7 +1942,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.23.0" +version = "4.25.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1697,9 +1950,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, + { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040 }, ] [package.optional-dependencies] @@ -1710,20 +1963,21 @@ format-nongpl = [ { name = "jsonpointer" }, { name = "rfc3339-validator" }, { name = "rfc3986-validator" }, + { name = "rfc3987-syntax" }, { name = "uri-template" }, { name = "webcolors" }, ] [[package]] name = "jsonschema-specifications" -version = "2024.10.1" +version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, ] [[package]] @@ -1780,24 +2034,24 @@ wheels = [ [[package]] name = "jupyter-core" -version = "5.7.2" +version = "5.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "platformdirs" }, - { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/11/b56381fa6c3f4cc5d2cf54a7dbf98ad9aa0b339ef7a601d6053538b079a7/jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9", size = 87629 } +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 }, + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, ] [[package]] name = "jupyter-events" -version = "0.11.0" +version = "0.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "packaging" }, { name = "python-json-logger" }, { name = "pyyaml" }, { name = "referencing" }, @@ -1805,26 +2059,26 @@ dependencies = [ { name = "rfc3986-validator" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/65/5791c8a979b5646ca29ea50e42b6708908b789f7ff389d1a03c1b93a1c54/jupyter_events-0.11.0.tar.gz", hash = "sha256:c0bc56a37aac29c1fbc3bcfbddb8c8c49533f9cf11f1c4e6adadba936574ab90", size = 62039 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/8c/9b65cb2cd4ea32d885993d5542244641590530836802a2e8c7449a4c61c9/jupyter_events-0.11.0-py3-none-any.whl", hash = "sha256:36399b41ce1ca45fe8b8271067d6a140ffa54cec4028e95491c93b78a855cacf", size = 19423 }, + { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430 }, ] [[package]] name = "jupyter-lsp" -version = "2.2.5" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/b4/3200b0b09c12bc3b72d943d923323c398eff382d1dcc7c0dbc8b74630e40/jupyter-lsp-2.2.5.tar.gz", hash = "sha256:793147a05ad446f809fd53ef1cd19a9f5256fd0a2d6b7ce943a982cb4f545001", size = 48741 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/e0/7bd7cff65594fd9936e2f9385701e44574fc7d721331ff676ce440b14100/jupyter_lsp-2.2.5-py3-none-any.whl", hash = "sha256:45fbddbd505f3fbfb0b6cb2f1bc5e15e83ab7c79cd6e89416b248cb3c00c11da", size = 69146 }, + { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687 }, ] [[package]] name = "jupyter-server" -version = "2.15.0" +version = "2.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1847,9 +2101,9 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/8c/df09d4ab646141f130f9977b32b206ba8615d1969b2eba6a2e84b7f89137/jupyter_server-2.15.0.tar.gz", hash = "sha256:9d446b8697b4f7337a1b7cdcac40778babdd93ba614b6d68ab1c0c918f1c4084", size = 725227 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/a2/89eeaf0bb954a123a909859fa507fa86f96eb61b62dc30667b60dbd5fdaf/jupyter_server-2.15.0-py3-none-any.whl", hash = "sha256:872d989becf83517012ee669f09604aa4a28097c0bd90b2f424310156c2cdae3", size = 385826 }, + { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221 }, ] [[package]] @@ -1867,7 +2121,7 @@ wheels = [ [[package]] name = "jupyterlab" -version = "4.3.4" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1885,9 +2139,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/45/1052f842e066902b1d78126df7e2269b1b9408991e1344e167b2e429f9e1/jupyterlab-4.3.4.tar.gz", hash = "sha256:f0bb9b09a04766e3423cccc2fc23169aa2ffedcdf8713e9e0fb33cac0b6859d0", size = 21797583 } +sdist = { url = "https://files.pythonhosted.org/packages/df/e5/4fa382a796a6d8e2cd867816b64f1ff27f906e43a7a83ad9eb389e448cd8/jupyterlab-4.5.0.tar.gz", hash = "sha256:aec33d6d8f1225b495ee2cf20f0514f45e6df8e360bdd7ac9bace0b7ac5177ea", size = 23989880 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/48/af57263e53cfc220e522de047aa0993f53bab734fe812af1e03e33ac6d7c/jupyterlab-4.3.4-py3-none-any.whl", hash = "sha256:b754c2601c5be6adf87cb5a1d8495d653ffb945f021939f77776acaa94dae952", size = 11665373 }, + { url = "https://files.pythonhosted.org/packages/6c/1e/5a4d5498eba382fee667ed797cf64ae5d1b13b04356df62f067f48bb0f61/jupyterlab-4.5.0-py3-none-any.whl", hash = "sha256:88e157c75c1afff64c7dc4b801ec471450b922a4eae4305211ddd40da8201c8a", size = 12380641 }, ] [[package]] @@ -1901,7 +2155,7 @@ wheels = [ [[package]] name = "jupyterlab-server" -version = "2.27.3" +version = "2.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "babel" }, @@ -1912,18 +2166,18 @@ dependencies = [ { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/c9/a883ce65eb27905ce77ace410d83587c82ea64dc85a48d1f7ed52bcfa68d/jupyterlab_server-2.27.3.tar.gz", hash = "sha256:eb36caca59e74471988f0ae25c77945610b887f777255aa21f8065def9e51ed4", size = 76173 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/09/2032e7d15c544a0e3cd831c51d77a8ca57f7555b2e1b2922142eddb02a84/jupyterlab_server-2.27.3-py3-none-any.whl", hash = "sha256:e697488f66c3db49df675158a77b3b017520d772c6e1548c7d9bcc5df7944ee4", size = 59700 }, + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, ] [[package]] name = "jupyterlab-widgets" -version = "3.0.13" +version = "3.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/59/73/fa26bbb747a9ea4fca6b01453aa22990d52ab62dd61384f1ac0dc9d4e7ba/jupyterlab_widgets-3.0.13.tar.gz", hash = "sha256:a2966d385328c1942b683a8cd96b89b8dd82c8b8f81dda902bb2bc06d46f5bed", size = 203556 } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/93/858e87edc634d628e5d752ba944c2833133a28fa87bb093e6832ced36a3e/jupyterlab_widgets-3.0.13-py3-none-any.whl", hash = "sha256:e3cda2c233ce144192f1e29914ad522b2f4c40e77214b0cc97377ca3d323db54", size = 214392 }, + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, ] [[package]] @@ -1949,31 +2203,28 @@ wheels = [ [[package]] name = "kiwisolver" -version = "1.4.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/5f/4d8e9e852d98ecd26cdf8eaf7ed8bc33174033bba5e07001b289f07308fd/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db", size = 124623 }, - { url = "https://files.pythonhosted.org/packages/1d/70/7f5af2a18a76fe92ea14675f8bd88ce53ee79e37900fa5f1a1d8e0b42998/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b", size = 66720 }, - { url = "https://files.pythonhosted.org/packages/c6/13/e15f804a142353aefd089fadc8f1d985561a15358c97aca27b0979cb0785/kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d", size = 65413 }, - { url = "https://files.pythonhosted.org/packages/ce/6d/67d36c4d2054e83fb875c6b59d0809d5c530de8148846b1370475eeeece9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d", size = 1650826 }, - { url = "https://files.pythonhosted.org/packages/de/c6/7b9bb8044e150d4d1558423a1568e4f227193662a02231064e3824f37e0a/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c", size = 1628231 }, - { url = "https://files.pythonhosted.org/packages/b6/38/ad10d437563063eaaedbe2c3540a71101fc7fb07a7e71f855e93ea4de605/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3", size = 1408938 }, - { url = "https://files.pythonhosted.org/packages/52/ce/c0106b3bd7f9e665c5f5bc1e07cc95b5dabd4e08e3dad42dbe2faad467e7/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed", size = 1422799 }, - { url = "https://files.pythonhosted.org/packages/d0/87/efb704b1d75dc9758087ba374c0f23d3254505edaedd09cf9d247f7878b9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f", size = 1354362 }, - { url = "https://files.pythonhosted.org/packages/eb/b3/fd760dc214ec9a8f208b99e42e8f0130ff4b384eca8b29dd0efc62052176/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff", size = 2222695 }, - { url = "https://files.pythonhosted.org/packages/a2/09/a27fb36cca3fc01700687cc45dae7a6a5f8eeb5f657b9f710f788748e10d/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d", size = 2370802 }, - { url = "https://files.pythonhosted.org/packages/3d/c3/ba0a0346db35fe4dc1f2f2cf8b99362fbb922d7562e5f911f7ce7a7b60fa/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c", size = 2334646 }, - { url = "https://files.pythonhosted.org/packages/41/52/942cf69e562f5ed253ac67d5c92a693745f0bed3c81f49fc0cbebe4d6b00/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605", size = 2467260 }, - { url = "https://files.pythonhosted.org/packages/32/26/2d9668f30d8a494b0411d4d7d4ea1345ba12deb6a75274d58dd6ea01e951/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e", size = 2288633 }, - { url = "https://files.pythonhosted.org/packages/98/99/0dd05071654aa44fe5d5e350729961e7bb535372935a45ac89a8924316e6/kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751", size = 71885 }, - { url = "https://files.pythonhosted.org/packages/6c/fc/822e532262a97442989335394d441cd1d0448c2e46d26d3e04efca84df22/kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271", size = 65175 }, - { url = "https://files.pythonhosted.org/packages/1f/f9/ae81c47a43e33b93b0a9819cac6723257f5da2a5a60daf46aa5c7226ea85/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a", size = 60403 }, - { url = "https://files.pythonhosted.org/packages/58/ca/f92b5cb6f4ce0c1ebfcfe3e2e42b96917e16f7090e45b21102941924f18f/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8", size = 58657 }, - { url = "https://files.pythonhosted.org/packages/80/28/ae0240f732f0484d3a4dc885d055653c47144bdf59b670aae0ec3c65a7c8/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0", size = 84948 }, - { url = "https://files.pythonhosted.org/packages/5d/eb/78d50346c51db22c7203c1611f9b513075f35c4e0e4877c5dde378d66043/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c", size = 81186 }, - { url = "https://files.pythonhosted.org/packages/43/f8/7259f18c77adca88d5f64f9a522792e178b2691f3748817a8750c2d216ef/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b", size = 80279 }, - { url = "https://files.pythonhosted.org/packages/3a/1d/50ad811d1c5dae091e4cf046beba925bcae0a610e79ae4c538f996f63ed5/kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b", size = 71762 }, +version = "1.4.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159 }, + { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578 }, + { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312 }, + { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458 }, + { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640 }, + { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074 }, + { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036 }, + { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310 }, + { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943 }, + { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488 }, + { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787 }, + { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730 }, + { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036 }, + { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183 }, + { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675 }, + { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277 }, + { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994 }, + { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744 }, ] [[package]] @@ -2017,6 +2268,15 @@ openai = [ { name = "openai" }, ] +[[package]] +name = "lark" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, +] + [[package]] name = "libclang" version = "18.1.1" @@ -2048,12 +2308,13 @@ wheels = [ [[package]] name = "litellm" -version = "1.80.5" +version = "1.80.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, { name = "fastuuid" }, + { name = "grpcio" }, { name = "httpx" }, { name = "importlib-metadata" }, { name = "jinja2" }, @@ -2064,40 +2325,47 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/b8/357544534bef87dd2858432f3cbd3a0e5cc267caebca5ea86b03618786c5/litellm-1.80.5.tar.gz", hash = "sha256:922791c264845d9ed59e540c8fa74a74d237c1b209568a05ffeacd8b51770deb", size = 11885764 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/3f/af532014449c3931ae6cad2d97d267dd43d0de006060a8cbf0962e004024/litellm-1.80.7.tar.gz", hash = "sha256:3977a8d195aef842d01c18bf9e22984829363c6a4b54daf9a43c9dd9f190b42c", size = 12023127 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/af/1d4693746ff9fbbe27a6e7d6394b801acf234e00c83f45ad1cb5bf2eaa6c/litellm-1.80.5-py3-none-any.whl", hash = "sha256:2ac5f4e88cd57ae056e00da8f872e1c2956653750929fba2fd9b007b400fdb77", size = 10671970 }, + { url = "https://files.pythonhosted.org/packages/54/e0/2e60a0c09235fd7b55297390c557923f3c35a9cf001914222c26a7857d2b/litellm-1.80.7-py3-none-any.whl", hash = "sha256:f7d993f78c1e0e4e1202b2a925cc6540b55b6e5fb055dd342d88b145ab3102ed", size = 10848321 }, ] [[package]] name = "lxml" -version = "5.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ce/2789e39eddf2b13fac29878bfa465f0910eb6b0096e29090e5176bc8cf43/lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656", size = 8124570 }, - { url = "https://files.pythonhosted.org/packages/24/a8/f4010166a25d41715527129af2675981a50d3bbf7df09c5d9ab8ca24fbf9/lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d", size = 4413042 }, - { url = "https://files.pythonhosted.org/packages/41/a4/7e45756cecdd7577ddf67a68b69c1db0f5ddbf0c9f65021ee769165ffc5a/lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a", size = 5139213 }, - { url = "https://files.pythonhosted.org/packages/02/e2/ecf845b12323c92748077e1818b64e8b4dba509a4cb12920b3762ebe7552/lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8", size = 4838814 }, - { url = "https://files.pythonhosted.org/packages/12/91/619f9fb72cf75e9ceb8700706f7276f23995f6ad757e6d400fbe35ca4990/lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330", size = 5425084 }, - { url = "https://files.pythonhosted.org/packages/25/3b/162a85a8f0fd2a3032ec3f936636911c6e9523a8e263fffcfd581ce98b54/lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965", size = 4875993 }, - { url = "https://files.pythonhosted.org/packages/43/af/dd3f58cc7d946da6ae42909629a2b1d5dd2d1b583334d4af9396697d6863/lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22", size = 5012462 }, - { url = "https://files.pythonhosted.org/packages/69/c1/5ea46b2d4c98f5bf5c83fffab8a0ad293c9bc74df9ecfbafef10f77f7201/lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b", size = 4815288 }, - { url = "https://files.pythonhosted.org/packages/1d/51/a0acca077ad35da458f4d3f729ef98effd2b90f003440d35fc36323f8ae6/lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7", size = 5472435 }, - { url = "https://files.pythonhosted.org/packages/4d/6b/0989c9368986961a6b0f55b46c80404c4b758417acdb6d87bfc3bd5f4967/lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8", size = 4976354 }, - { url = "https://files.pythonhosted.org/packages/05/9e/87492d03ff604fbf656ed2bf3e2e8d28f5d58ea1f00ff27ac27b06509079/lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32", size = 5029973 }, - { url = "https://files.pythonhosted.org/packages/f9/cc/9ae1baf5472af88e19e2c454b3710c1be9ecafb20eb474eeabcd88a055d2/lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86", size = 4888837 }, - { url = "https://files.pythonhosted.org/packages/d2/10/5594ffaec8c120d75b17e3ad23439b740a51549a9b5fd7484b2179adfe8f/lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5", size = 5530555 }, - { url = "https://files.pythonhosted.org/packages/ea/9b/de17f05377c8833343b629905571fb06cff2028f15a6f58ae2267662e341/lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03", size = 5405314 }, - { url = "https://files.pythonhosted.org/packages/8a/b4/227be0f1f3cca8255925985164c3838b8b36e441ff0cc10c1d3c6bdba031/lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7", size = 5079303 }, - { url = "https://files.pythonhosted.org/packages/5c/ee/19abcebb7fc40319bb71cd6adefa1ad94d09b5660228715854d6cc420713/lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80", size = 3475126 }, - { url = "https://files.pythonhosted.org/packages/a1/35/183d32551447e280032b2331738cd850da435a42f850b71ebeaab42c1313/lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3", size = 3805065 }, - { url = "https://files.pythonhosted.org/packages/99/f7/b73a431c8500565aa500e99e60b448d305eaf7c0b4c893c7c5a8a69cc595/lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c", size = 3925431 }, - { url = "https://files.pythonhosted.org/packages/db/48/4a206623c0d093d0e3b15f415ffb4345b0bdf661a3d0b15a112948c033c7/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a", size = 4216683 }, - { url = "https://files.pythonhosted.org/packages/54/47/577820c45dd954523ae8453b632d91e76da94ca6d9ee40d8c98dd86f916b/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005", size = 4326732 }, - { url = "https://files.pythonhosted.org/packages/68/de/96cb6d3269bc994b4f5ede8ca7bf0840f5de0a278bc6e50cb317ff71cafa/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce", size = 4218377 }, - { url = "https://files.pythonhosted.org/packages/a5/43/19b1ef6cbffa4244a217f95cc5f41a6cb4720fed33510a49670b03c5f1a0/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83", size = 4351237 }, - { url = "https://files.pythonhosted.org/packages/ba/b2/6a22fb5c0885da3b00e116aee81f0b829ec9ac8f736cd414b4a09413fc7d/lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba", size = 3487557 }, +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589 }, + { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671 }, + { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961 }, + { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087 }, + { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620 }, + { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664 }, + { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397 }, + { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178 }, + { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148 }, + { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035 }, + { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111 }, + { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662 }, + { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973 }, + { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953 }, + { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695 }, + { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051 }, + { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264 }, + { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435 }, + { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913 }, + { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357 }, + { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295 }, + { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913 }, +] + +[[package]] +name = "magicattr" +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/7e/76b7e0c391bee7e9273725c29c8fe41c4df62a215ce58aa8e3518baee0bb/magicattr-0.1.6-py2.py3-none-any.whl", hash = "sha256:d96b18ee45b5ee83b09c17e15d3459a64de62d538808c2f71182777dd9dbbbdf", size = 4664 }, ] [[package]] @@ -2110,58 +2378,59 @@ wheels = [ [[package]] name = "mako" -version = "1.3.8" +version = "1.3.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/d9/8518279534ed7dace1795d5a47e49d5299dd0994eed1053996402a8902f9/mako-1.3.8.tar.gz", hash = "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8", size = 392069 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/bf/7a6a36ce2e4cafdfb202752be68850e22607fccd692847c45c1ae3c17ba6/Mako-1.3.8-py3-none-any.whl", hash = "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627", size = 78569 }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, ] [[package]] name = "markdown" -version = "3.7" +version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, + { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678 }, ] [[package]] name = "markdown-it-py" -version = "3.0.0" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, ] [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631 }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057 }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050 }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681 }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705 }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524 }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282 }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745 }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571 }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056 }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932 }, ] [[package]] name = "matplotlib" -version = "3.10.0" +version = "3.10.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -2174,29 +2443,29 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/dd/fa2e1a45fce2d09f4aea3cee169760e672c8262325aa5796c49d543dc7e6/matplotlib-3.10.0.tar.gz", hash = "sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278", size = 36686418 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/e2/d2d5295be2f44c678ebaf3544ba32d20c1f9ef08c49fe47f496180e1db15/matplotlib-3.10.7.tar.gz", hash = "sha256:a06ba7e2a2ef9131c79c49e63dad355d2d878413a0376c1727c8b9335ff731c7", size = 34804865 } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/ec/3cdff7b5239adaaacefcc4f77c316dfbbdf853c4ed2beec467e0fec31b9f/matplotlib-3.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6", size = 8160551 }, - { url = "https://files.pythonhosted.org/packages/41/f2/b518f2c7f29895c9b167bf79f8529c63383ae94eaf49a247a4528e9a148d/matplotlib-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e", size = 8034853 }, - { url = "https://files.pythonhosted.org/packages/ed/8d/45754b4affdb8f0d1a44e4e2bcd932cdf35b256b60d5eda9f455bb293ed0/matplotlib-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:607b16c8a73943df110f99ee2e940b8a1cbf9714b65307c040d422558397dac5", size = 8446724 }, - { url = "https://files.pythonhosted.org/packages/09/5a/a113495110ae3e3395c72d82d7bc4802902e46dc797f6b041e572f195c56/matplotlib-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01d2b19f13aeec2e759414d3bfe19ddfb16b13a1250add08d46d5ff6f9be83c6", size = 8583905 }, - { url = "https://files.pythonhosted.org/packages/12/b1/8b1655b4c9ed4600c817c419f7eaaf70082630efd7556a5b2e77a8a3cdaf/matplotlib-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e6c6461e1fc63df30bf6f80f0b93f5b6784299f721bc28530477acd51bfc3d1", size = 9395223 }, - { url = "https://files.pythonhosted.org/packages/5a/85/b9a54d64585a6b8737a78a61897450403c30f39e0bd3214270bb0b96f002/matplotlib-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:994c07b9d9fe8d25951e3202a68c17900679274dadfc1248738dcfa1bd40d7f3", size = 8025355 }, - { url = "https://files.pythonhosted.org/packages/32/5f/29def7ce4e815ab939b56280976ee35afffb3bbdb43f332caee74cb8c951/matplotlib-3.10.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81713dd0d103b379de4516b861d964b1d789a144103277769238c732229d7f03", size = 8155500 }, - { url = "https://files.pythonhosted.org/packages/de/6d/d570383c9f7ca799d0a54161446f9ce7b17d6c50f2994b653514bcaa108f/matplotlib-3.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:359f87baedb1f836ce307f0e850d12bb5f1936f70d035561f90d41d305fdacea", size = 8032398 }, - { url = "https://files.pythonhosted.org/packages/c9/b4/680aa700d99b48e8c4393fa08e9ab8c49c0555ee6f4c9c0a5e8ea8dfde5d/matplotlib-3.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae80dc3a4add4665cf2faa90138384a7ffe2a4e37c58d83e115b54287c4f06ef", size = 8587361 }, + { url = "https://files.pythonhosted.org/packages/6c/87/3932d5778ab4c025db22710b61f49ccaed3956c5cf46ffb2ffa7492b06d9/matplotlib-3.10.7-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ac81eee3b7c266dd92cee1cd658407b16c57eed08c7421fa354ed68234de380", size = 8247141 }, + { url = "https://files.pythonhosted.org/packages/45/a8/bfed45339160102bce21a44e38a358a1134a5f84c26166de03fb4a53208f/matplotlib-3.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:667ecd5d8d37813a845053d8f5bf110b534c3c9f30e69ebd25d4701385935a6d", size = 8107995 }, + { url = "https://files.pythonhosted.org/packages/e2/3c/5692a2d9a5ba848fda3f48d2b607037df96460b941a59ef236404b39776b/matplotlib-3.10.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc1c51b846aca49a5a8b44fbba6a92d583a35c64590ad9e1e950dc88940a4297", size = 8680503 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/86ace53c48b05d0e6e9c127b2ace097434901f3e7b93f050791c8243201a/matplotlib-3.10.7-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a11c2e9e72e7de09b7b72e62f3df23317c888299c875e2b778abf1eda8c0a42", size = 9514982 }, + { url = "https://files.pythonhosted.org/packages/a6/81/ead71e2824da8f72640a64166d10e62300df4ae4db01a0bac56c5b39fa51/matplotlib-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f19410b486fdd139885ace124e57f938c1e6a3210ea13dd29cab58f5d4bc12c7", size = 9566429 }, + { url = "https://files.pythonhosted.org/packages/65/7d/954b3067120456f472cce8fdcacaf4a5fcd522478db0c37bb243c7cb59dd/matplotlib-3.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:b498e9e4022f93de2d5a37615200ca01297ceebbb56fe4c833f46862a490f9e3", size = 8108174 }, + { url = "https://files.pythonhosted.org/packages/1e/6c/a9bcf03e9afb2a873e0a5855f79bce476d1023f26f8212969f2b7504756c/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5c09cf8f2793f81368f49f118b6f9f937456362bee282eac575cca7f84cda537", size = 8241204 }, + { url = "https://files.pythonhosted.org/packages/5b/fd/0e6f5aa762ed689d9fa8750b08f1932628ffa7ed30e76423c399d19407d2/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:de66744b2bb88d5cd27e80dfc2ec9f0517d0a46d204ff98fe9e5f2864eb67657", size = 8104607 }, + { url = "https://files.pythonhosted.org/packages/b9/a9/21c9439d698fac5f0de8fc68b2405b738ed1f00e1279c76f2d9aa5521ead/matplotlib-3.10.7-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53cc80662dd197ece414dd5b66e07370201515a3eaf52e7c518c68c16814773b", size = 8682257 }, ] [[package]] name = "matplotlib-inline" -version = "0.1.7" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516 }, ] [[package]] @@ -2210,14 +2479,27 @@ wheels = [ [[package]] name = "mistune" -version = "3.1.0" +version = "3.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/6e/96fc7cb3288666c5de2c396eb0e338dc95f7a8e4920e43e38783a22d0084/mistune-3.1.0.tar.gz", hash = "sha256:dbcac2f78292b9dc066cd03b7a3a26b62d85f8159f2ea5fd28e55df79908d667", size = 94401 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/02/a7fb8b21d4d55ac93cdcde9d3638da5dd0ebdd3a4fed76c7725e10b81cbe/mistune-3.1.4.tar.gz", hash = "sha256:b5a7f801d389f724ec702840c11d8fc48f2b33519102fc7ee739e8177b672164", size = 94588 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/f0/8282d9641415e9e33df173516226b404d367a0fc55e1a60424a152913abc/mistune-3.1.4-py3-none-any.whl", hash = "sha256:93691da911e5d9d2e23bc54472892aff676df27a75274962ff9edc210364266d", size = 53481 }, +] + +[[package]] +name = "ml-collections" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/b3/743ffc3f59da380da504d84ccd1faf9a857a1445991ff19bf2ec754163c2/mistune-3.1.0-py3-none-any.whl", hash = "sha256:b05198cf6d671b3deba6c87ec6cf0d4eb7b72c524636eddb6dbf13823b52cee1", size = 53694 }, + { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, ] [[package]] @@ -2235,24 +2517,24 @@ wheels = [ [[package]] name = "more-itertools" -version = "10.5.0" +version = "10.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/78/65922308c4248e0eb08ebcbe67c95d48615cc6f27854b6f2e57143e9178f/more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6", size = 121020 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431 } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/7e/3a64597054a70f7c86eb0a7d4fc315b8c1ab932f64883a297bdffeb5f967/more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", size = 60952 }, + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667 }, ] [[package]] name = "mpld3" -version = "0.5.10" +version = "0.5.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jinja2" }, { name = "matplotlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/58/19378f4189a034eb3efc17b133426b8551af1d3b2c70d641a63124579629/mpld3-0.5.10.tar.gz", hash = "sha256:a478eb404fa5212505c59133cf272cd9a94105872e605597720e7f84de38fbc7", size = 1027709 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/6a/e3691bcc47485f38b09853207c928130571821d187cf174eed5418d45e82/mpld3-0.5.10-py3-none-any.whl", hash = "sha256:80877acce87ea447380fad7374668737505c8c0684aab05238e7c5dc1fab38c1", size = 202561 }, + { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, ] [[package]] @@ -2266,45 +2548,58 @@ wheels = [ [[package]] name = "multidict" -version = "6.1.0" +version = "6.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/68/259dee7fd14cf56a17c554125e534f6274c2860159692a414d0b402b9a6d/multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", size = 48628 }, - { url = "https://files.pythonhosted.org/packages/50/79/53ba256069fe5386a4a9e80d4e12857ced9de295baf3e20c68cdda746e04/multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", size = 29327 }, - { url = "https://files.pythonhosted.org/packages/ff/10/71f1379b05b196dae749b5ac062e87273e3f11634f447ebac12a571d90ae/multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", size = 29689 }, - { url = "https://files.pythonhosted.org/packages/71/45/70bac4f87438ded36ad4793793c0095de6572d433d98575a5752629ef549/multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", size = 126639 }, - { url = "https://files.pythonhosted.org/packages/80/cf/17f35b3b9509b4959303c05379c4bfb0d7dd05c3306039fc79cf035bbac0/multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", size = 134315 }, - { url = "https://files.pythonhosted.org/packages/ef/1f/652d70ab5effb33c031510a3503d4d6efc5ec93153562f1ee0acdc895a57/multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", size = 129471 }, - { url = "https://files.pythonhosted.org/packages/a6/64/2dd6c4c681688c0165dea3975a6a4eab4944ea30f35000f8b8af1df3148c/multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", size = 124585 }, - { url = "https://files.pythonhosted.org/packages/87/56/e6ee5459894c7e554b57ba88f7257dc3c3d2d379cb15baaa1e265b8c6165/multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", size = 116957 }, - { url = "https://files.pythonhosted.org/packages/36/9e/616ce5e8d375c24b84f14fc263c7ef1d8d5e8ef529dbc0f1df8ce71bb5b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db", size = 128609 }, - { url = "https://files.pythonhosted.org/packages/8c/4f/4783e48a38495d000f2124020dc96bacc806a4340345211b1ab6175a6cb4/multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", size = 123016 }, - { url = "https://files.pythonhosted.org/packages/3e/b3/4950551ab8fc39862ba5e9907dc821f896aa829b4524b4deefd3e12945ab/multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", size = 133542 }, - { url = "https://files.pythonhosted.org/packages/96/4d/f0ce6ac9914168a2a71df117935bb1f1781916acdecbb43285e225b484b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", size = 130163 }, - { url = "https://files.pythonhosted.org/packages/be/72/17c9f67e7542a49dd252c5ae50248607dfb780bcc03035907dafefb067e3/multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", size = 126832 }, - { url = "https://files.pythonhosted.org/packages/71/9f/72d719e248cbd755c8736c6d14780533a1606ffb3fbb0fbd77da9f0372da/multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", size = 26402 }, - { url = "https://files.pythonhosted.org/packages/04/5a/d88cd5d00a184e1ddffc82aa2e6e915164a6d2641ed3606e766b5d2f275a/multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", size = 28800 }, - { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153 }, + { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993 }, + { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607 }, + { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847 }, + { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616 }, + { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333 }, + { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239 }, + { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618 }, + { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655 }, + { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245 }, + { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523 }, + { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129 }, + { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999 }, + { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711 }, + { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504 }, + { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422 }, + { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050 }, + { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153 }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317 }, ] [[package]] name = "multiprocess" -version = "0.70.16" +version = "0.70.18" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603 } +sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083 }, + { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128 }, + { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132 }, + { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948 }, + { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636 }, + { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478 }, +] + +[[package]] +name = "narwhals" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/ea/f82ef99ced4d03c33bb314c9b84a08a0a86c448aaa11ffd6256b99538aa5/narwhals-2.13.0.tar.gz", hash = "sha256:ee94c97f4cf7cfeebbeca8d274784df8b3d7fd3f955ce418af998d405576fdd9", size = 594555 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/76/6e712a2623d146d314f17598df5de7224c85c0060ef63fd95cc15a25b3fa/multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee", size = 134980 }, - { url = "https://files.pythonhosted.org/packages/0f/ab/1e6e8009e380e22254ff539ebe117861e5bdb3bff1fc977920972237c6c7/multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec", size = 134982 }, - { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824 }, - { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628 }, - { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, + { url = "https://files.pythonhosted.org/packages/87/0d/1861d1599571974b15b025e12b142d8e6b42ad66c8a07a89cb0fc21f1e03/narwhals-2.13.0-py3-none-any.whl", hash = "sha256:9b795523c179ca78204e3be53726da374168f906e38de2ff174c2363baaaf481", size = 426407 }, ] [[package]] @@ -2324,7 +2619,7 @@ wheels = [ [[package]] name = "nbconvert" -version = "7.16.5" +version = "7.16.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, @@ -2342,9 +2637,9 @@ dependencies = [ { name = "pygments" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/2c/d026c0367f2be2463d4c2f5b538e28add2bc67bc13730abb7f364ae4eb8b/nbconvert-7.16.5.tar.gz", hash = "sha256:c83467bb5777fdfaac5ebbb8e864f300b277f68692ecc04d6dab72f2d8442344", size = 856367 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/9e/2dcc9fe00cf55d95a8deae69384e9cea61816126e345754f6c75494d32ec/nbconvert-7.16.5-py3-none-any.whl", hash = "sha256:e12eac052d6fd03040af4166c563d76e7aeead2e9aadf5356db552a1784bd547", size = 258061 }, + { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525 }, ] [[package]] @@ -2364,14 +2659,14 @@ wheels = [ [[package]] name = "nbstripout" -version = "0.8.0" +version = "0.8.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nbformat" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/34/e0a9ae627f02d40861656be351da44647406d7f0ce5cbc09e85abba52935/nbstripout-0.8.0.tar.gz", hash = "sha256:4b9b563c3704f9b59067627bec7d0d5c7437527ab6c3a72dd3cf895d46bf5a44", size = 26018 } +sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/f2/7aca174fb55ea04eb5918da199ffb6aa03258ee23e169d10a4d2a8cd4838/nbstripout-0.8.0-py2.py3-none-any.whl", hash = "sha256:b37f7b297fc6c02647d387d1049e4be8d0ecbf74640e502dce36ae93120ad420", size = 16237 }, + { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122 }, ] [[package]] @@ -2403,7 +2698,7 @@ wheels = [ [[package]] name = "notebook" -version = "7.3.2" +version = "7.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, @@ -2412,9 +2707,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/04/ac488379d5afef43402b3fb4be2857db1a09804fecf98b9b714c741b225b/notebook-7.3.2.tar.gz", hash = "sha256:705e83a1785f45b383bf3ee13cb76680b92d24f56fb0c7d2136fe1d850cd3ca8", size = 12781804 } +sdist = { url = "https://files.pythonhosted.org/packages/89/ac/a97041621250a4fc5af379fb377942841eea2ca146aab166b8fcdfba96c2/notebook-7.5.0.tar.gz", hash = "sha256:3b27eaf9913033c28dde92d02139414c608992e1df4b969c843219acf2ff95e4", size = 14052074 } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/9b/76e50ee18f183ea5fe1784a9eeaa50f2c71802e4740d6e959592b0993298/notebook-7.3.2-py3-none-any.whl", hash = "sha256:e5f85fc59b69d3618d73cf27544418193ff8e8058d5bf61d315ce4f473556288", size = 13163630 }, + { url = "https://files.pythonhosted.org/packages/73/96/00df2a4760f10f5af0f45c4955573cae6189931f9a30265a35865f8c1031/notebook-7.5.0-py3-none-any.whl", hash = "sha256:3300262d52905ca271bd50b22617681d95f08a8360d099e097726e6d2efb5811", size = 14460968 }, ] [[package]] @@ -2596,11 +2891,11 @@ wheels = [ [[package]] name = "oauthlib" -version = "3.2.2" +version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 }, + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 }, ] [[package]] @@ -2636,7 +2931,7 @@ wheels = [ [[package]] name = "openai" -version = "2.8.1" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2648,9 +2943,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/e4/42591e356f1d53c568418dc7e30dcda7be31dd5a4d570bca22acb0525862/openai-2.8.1.tar.gz", hash = "sha256:cb1b79eef6e809f6da326a7ef6038719e35aa944c42d081807bfa1be8060f15f", size = 602490 } +sdist = { url = "https://files.pythonhosted.org/packages/09/48/516290f38745cc1e72856f50e8afed4a7f9ac396a5a18f39e892ab89dfc2/openai-2.9.0.tar.gz", hash = "sha256:b52ec65727fc8f1eed2fbc86c8eac0998900c7ef63aa2eb5c24b69717c56fa5f", size = 608202 } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/4f/dbc0c124c40cb390508a82770fb9f6e3ed162560181a85089191a851c59a/openai-2.8.1-py3-none-any.whl", hash = "sha256:c6c3b5a04994734386e8dad3c00a393f56d3b68a27cd2e8acae91a59e4122463", size = 1022688 }, + { url = "https://files.pythonhosted.org/packages/59/fd/ae2da789cd923dd033c99b8d544071a827c92046b150db01cfa5cea5b3fd/openai-2.9.0-py3-none-any.whl", hash = "sha256:0d168a490fbb45630ad508a6f3022013c155a68fd708069b6a1a01a5e8f0ffad", size = 1030836 }, ] [[package]] @@ -2712,11 +3007,11 @@ wheels = [ [[package]] name = "packaging" -version = "24.2" +version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] [[package]] @@ -2749,11 +3044,11 @@ wheels = [ [[package]] name = "parso" -version = "0.8.4" +version = "0.8.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668 }, ] [[package]] @@ -2785,59 +3080,52 @@ wheels = [ [[package]] name = "pillow" -version = "11.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/1c/2dcea34ac3d7bc96a1fd1bd0a6e06a57c67167fec2cff8d95d88229a8817/pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", size = 3229983 }, - { url = "https://files.pythonhosted.org/packages/14/ca/6bec3df25e4c88432681de94a3531cc738bd85dea6c7aa6ab6f81ad8bd11/pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", size = 3101831 }, - { url = "https://files.pythonhosted.org/packages/d4/2c/668e18e5521e46eb9667b09e501d8e07049eb5bfe39d56be0724a43117e6/pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", size = 4314074 }, - { url = "https://files.pythonhosted.org/packages/02/80/79f99b714f0fc25f6a8499ecfd1f810df12aec170ea1e32a4f75746051ce/pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", size = 4394933 }, - { url = "https://files.pythonhosted.org/packages/81/aa/8d4ad25dc11fd10a2001d5b8a80fdc0e564ac33b293bdfe04ed387e0fd95/pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", size = 4353349 }, - { url = "https://files.pythonhosted.org/packages/84/7a/cd0c3eaf4a28cb2a74bdd19129f7726277a7f30c4f8424cd27a62987d864/pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", size = 4476532 }, - { url = "https://files.pythonhosted.org/packages/8f/8b/a907fdd3ae8f01c7670dfb1499c53c28e217c338b47a813af8d815e7ce97/pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", size = 4279789 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/9f139d9e8cccd661c3efbf6898967a9a337eb2e9be2b454ba0a09533100d/pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", size = 4413131 }, - { url = "https://files.pythonhosted.org/packages/a8/68/0d8d461f42a3f37432203c8e6df94da10ac8081b6d35af1c203bf3111088/pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", size = 2291213 }, - { url = "https://files.pythonhosted.org/packages/14/81/d0dff759a74ba87715509af9f6cb21fa21d93b02b3316ed43bda83664db9/pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", size = 2625725 }, - { url = "https://files.pythonhosted.org/packages/ce/1f/8d50c096a1d58ef0584ddc37e6f602828515219e9d2428e14ce50f5ecad1/pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", size = 2375213 }, - { url = "https://files.pythonhosted.org/packages/fa/c5/389961578fb677b8b3244fcd934f720ed25a148b9a5cc81c91bdf59d8588/pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", size = 3198345 }, - { url = "https://files.pythonhosted.org/packages/c4/fa/803c0e50ffee74d4b965229e816af55276eac1d5806712de86f9371858fd/pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", size = 3072938 }, - { url = "https://files.pythonhosted.org/packages/dc/67/2a3a5f8012b5d8c63fe53958ba906c1b1d0482ebed5618057ef4d22f8076/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", size = 3400049 }, - { url = "https://files.pythonhosted.org/packages/e5/a0/514f0d317446c98c478d1872497eb92e7cde67003fed74f696441e647446/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", size = 3422431 }, - { url = "https://files.pythonhosted.org/packages/cd/00/20f40a935514037b7d3f87adfc87d2c538430ea625b63b3af8c3f5578e72/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", size = 3446208 }, - { url = "https://files.pythonhosted.org/packages/28/3c/7de681727963043e093c72e6c3348411b0185eab3263100d4490234ba2f6/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", size = 3509746 }, - { url = "https://files.pythonhosted.org/packages/41/67/936f9814bdd74b2dfd4822f1f7725ab5d8ff4103919a1664eb4874c58b2f/pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", size = 2626353 }, +version = "12.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/08/26e68b6b5da219c2a2cb7b563af008b53bb8e6b6fcb3fa40715fcdb2523a/pillow-12.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:3adfb466bbc544b926d50fe8f4a4e6abd8c6bffd28a26177594e6e9b2b76572b", size = 5289809 }, + { url = "https://files.pythonhosted.org/packages/cb/e9/4e58fb097fb74c7b4758a680aacd558810a417d1edaa7000142976ef9d2f/pillow-12.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ac11e8ea4f611c3c0147424eae514028b5e9077dd99ab91e1bd7bc33ff145e1", size = 4650606 }, + { url = "https://files.pythonhosted.org/packages/4b/e0/1fa492aa9f77b3bc6d471c468e62bfea1823056bf7e5e4f1914d7ab2565e/pillow-12.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d49e2314c373f4c2b39446fb1a45ed333c850e09d0c59ac79b72eb3b95397363", size = 6221023 }, + { url = "https://files.pythonhosted.org/packages/c1/09/4de7cd03e33734ccd0c876f0251401f1314e819cbfd89a0fcb6e77927cc6/pillow-12.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7b2a63fd6d5246349f3d3f37b14430d73ee7e8173154461785e43036ffa96ca", size = 8024937 }, + { url = "https://files.pythonhosted.org/packages/2e/69/0688e7c1390666592876d9d474f5e135abb4acb39dcb583c4dc5490f1aff/pillow-12.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d64317d2587c70324b79861babb9c09f71fbb780bad212018874b2c013d8600e", size = 6334139 }, + { url = "https://files.pythonhosted.org/packages/ed/1c/880921e98f525b9b44ce747ad1ea8f73fd7e992bafe3ca5e5644bf433dea/pillow-12.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d77153e14b709fd8b8af6f66a3afbb9ed6e9fc5ccf0b6b7e1ced7b036a228782", size = 7026074 }, + { url = "https://files.pythonhosted.org/packages/28/03/96f718331b19b355610ef4ebdbbde3557c726513030665071fd025745671/pillow-12.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32ed80ea8a90ee3e6fa08c21e2e091bba6eda8eccc83dbc34c95169507a91f10", size = 6448852 }, + { url = "https://files.pythonhosted.org/packages/3a/a0/6a193b3f0cc9437b122978d2c5cbce59510ccf9a5b48825096ed7472da2f/pillow-12.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c828a1ae702fc712978bda0320ba1b9893d99be0badf2647f693cc01cf0f04fa", size = 7117058 }, + { url = "https://files.pythonhosted.org/packages/a7/c4/043192375eaa4463254e8e61f0e2ec9a846b983929a8d0a7122e0a6d6fff/pillow-12.0.0-cp310-cp310-win32.whl", hash = "sha256:bd87e140e45399c818fac4247880b9ce719e4783d767e030a883a970be632275", size = 6295431 }, + { url = "https://files.pythonhosted.org/packages/92/c6/c2f2fc7e56301c21827e689bb8b0b465f1b52878b57471a070678c0c33cd/pillow-12.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:455247ac8a4cfb7b9bc45b7e432d10421aea9fc2e74d285ba4072688a74c2e9d", size = 7000412 }, + { url = "https://files.pythonhosted.org/packages/b2/d2/5f675067ba82da7a1c238a73b32e3fd78d67f9d9f80fbadd33a40b9c0481/pillow-12.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6ace95230bfb7cd79ef66caa064bbe2f2a1e63d93471c3a2e1f1348d9f22d6b7", size = 2435903 }, ] [[package]] name = "pip" -version = "24.3.1" +version = "25.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/b422acd212ad7eedddaf7981eee6e5de085154ff726459cf2da7c5a184c1/pip-24.3.1.tar.gz", hash = "sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99", size = 1931073 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/7d/500c9ad20238fcfcb4cb9243eede163594d7020ce87bd9610c9e02771876/pip-24.3.1-py3-none-any.whl", hash = "sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed", size = 1822182 }, + { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622 }, ] [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731 }, ] [[package]] name = "plotly" -version = "5.24.1" +version = "6.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "narwhals" }, { name = "packaging" }, - { name = "tenacity" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/4f/428f6d959818d7425a94c190a6b26fbc58035cbef40bf249be0b62a9aedd/plotly-5.24.1.tar.gz", hash = "sha256:dbc8ac8339d248a4bcc36e08a5659bacfe1b079390b8953533f4eb22169b4bae", size = 9479398 } +sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/ae/580600f441f6fc05218bd6c9d5794f4aef072a7d9093b291f1c50a9db8bc/plotly-5.24.1-py3-none-any.whl", hash = "sha256:f67073a1e637eb0dc3e46324d9d51e2fe76e9727c892dde64ddf1e1b51f29089", size = 19054220 }, + { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174 }, ] [[package]] @@ -2848,7 +3136,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d [[package]] name = "pre-commit" -version = "4.0.1" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -2857,55 +3145,66 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428 } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 }, + { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429 }, ] [[package]] name = "prometheus-client" -version = "0.21.1" +version = "0.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/14/7d0f567991f3a9af8d1cd4f619040c93b68f09a02b6d0b6ab1b2d1ded5fe/prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb", size = 78551 } +sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/c2/ab7d37426c179ceb9aeb109a85cda8948bb269b7561a0be870cc656eefe4/prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301", size = 54682 }, + { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145 }, ] [[package]] name = "prompt-toolkit" -version = "3.0.50" +version = "3.0.52" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, ] [[package]] name = "propcache" -version = "0.2.1" +version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/c8/2a13f78d82211490855b2fb303b6721348d0787fdd9a12ac46d99d3acde1/propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", size = 41735 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534 }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526 }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263 }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012 }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491 }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319 }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856 }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241 }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552 }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778 }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047 }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093 }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638 }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229 }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, +] + +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/a5/0ea64c9426959ef145a938e38c832fc551843481d356713ececa9a8a64e8/propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6", size = 79296 }, - { url = "https://files.pythonhosted.org/packages/76/5a/916db1aba735f55e5eca4733eea4d1973845cf77dfe67c2381a2ca3ce52d/propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2", size = 45622 }, - { url = "https://files.pythonhosted.org/packages/2d/62/685d3cf268b8401ec12b250b925b21d152b9d193b7bffa5fdc4815c392c2/propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea", size = 45133 }, - { url = "https://files.pythonhosted.org/packages/4d/3d/31c9c29ee7192defc05aa4d01624fd85a41cf98e5922aaed206017329944/propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212", size = 204809 }, - { url = "https://files.pythonhosted.org/packages/10/a1/e4050776f4797fc86140ac9a480d5dc069fbfa9d499fe5c5d2fa1ae71f07/propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3", size = 219109 }, - { url = "https://files.pythonhosted.org/packages/c9/c0/e7ae0df76343d5e107d81e59acc085cea5fd36a48aa53ef09add7503e888/propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d", size = 217368 }, - { url = "https://files.pythonhosted.org/packages/fc/e1/e0a2ed6394b5772508868a977d3238f4afb2eebaf9976f0b44a8d347ad63/propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634", size = 205124 }, - { url = "https://files.pythonhosted.org/packages/50/c1/e388c232d15ca10f233c778bbdc1034ba53ede14c207a72008de45b2db2e/propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2", size = 195463 }, - { url = "https://files.pythonhosted.org/packages/0a/fd/71b349b9def426cc73813dbd0f33e266de77305e337c8c12bfb0a2a82bfb/propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958", size = 198358 }, - { url = "https://files.pythonhosted.org/packages/02/f2/d7c497cd148ebfc5b0ae32808e6c1af5922215fe38c7a06e4e722fe937c8/propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c", size = 195560 }, - { url = "https://files.pythonhosted.org/packages/bb/57/f37041bbe5e0dfed80a3f6be2612a3a75b9cfe2652abf2c99bef3455bbad/propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583", size = 196895 }, - { url = "https://files.pythonhosted.org/packages/83/36/ae3cc3e4f310bff2f064e3d2ed5558935cc7778d6f827dce74dcfa125304/propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf", size = 207124 }, - { url = "https://files.pythonhosted.org/packages/8c/c4/811b9f311f10ce9d31a32ff14ce58500458443627e4df4ae9c264defba7f/propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034", size = 210442 }, - { url = "https://files.pythonhosted.org/packages/18/dd/a1670d483a61ecac0d7fc4305d91caaac7a8fc1b200ea3965a01cf03bced/propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b", size = 203219 }, - { url = "https://files.pythonhosted.org/packages/f9/2d/30ced5afde41b099b2dc0c6573b66b45d16d73090e85655f1a30c5a24e07/propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4", size = 40313 }, - { url = "https://files.pythonhosted.org/packages/23/84/bd9b207ac80da237af77aa6e153b08ffa83264b1c7882495984fcbfcf85c/propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba", size = 44428 }, - { url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 }, + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, ] [[package]] @@ -2968,17 +3267,17 @@ wheels = [ [[package]] name = "pyarrow" -version = "19.0.0" +version = "22.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/01/fe1fd04744c2aa038e5a11c7a4adb3d62bce09798695e54f7274b5977134/pyarrow-19.0.0.tar.gz", hash = "sha256:8d47c691765cf497aaeed4954d226568563f1b3b74ff61139f2d77876717084b", size = 1129096 } +sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/02/1ad80ffd3c558916858a49c83b6e494a9d93009bbebc603cf0cb8263bea7/pyarrow-19.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c318eda14f6627966997a7d8c374a87d084a94e4e38e9abbe97395c215830e0c", size = 30686262 }, - { url = "https://files.pythonhosted.org/packages/1b/f0/adab5f142eb8203db8bfbd3a816816e37a85423ae684567e7f3555658315/pyarrow-19.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:62ef8360ff256e960f57ce0299090fb86423afed5e46f18f1225f960e05aae3d", size = 32100005 }, - { url = "https://files.pythonhosted.org/packages/94/8b/e674083610e5efc48d2f205c568d842cdfdf683d12f9ff0d546e38757722/pyarrow-19.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2795064647add0f16563e57e3d294dbfc067b723f0fd82ecd80af56dad15f503", size = 41144815 }, - { url = "https://files.pythonhosted.org/packages/d5/fb/2726241a792b7f8a58789e5a63d1be9a5a4059206318fd0ff9485a578952/pyarrow-19.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a218670b26fb1bc74796458d97bcab072765f9b524f95b2fccad70158feb8b17", size = 42180380 }, - { url = "https://files.pythonhosted.org/packages/7d/09/7aef12446d8e7002dfc07bb7bc71f594c1d5844ca78b364a49f07efb65b1/pyarrow-19.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:66732e39eaa2247996a6b04c8aa33e3503d351831424cdf8d2e9a0582ac54b34", size = 40515021 }, - { url = "https://files.pythonhosted.org/packages/31/55/f05fc5608cc96060c2b24de505324d641888bd62d4eed2fa1dacd872a1e1/pyarrow-19.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e675a3ad4732b92d72e4d24009707e923cab76b0d088e5054914f11a797ebe44", size = 42067488 }, - { url = "https://files.pythonhosted.org/packages/f0/01/097653cec7a944c16313cb748a326771133c142034b252076bd84743b98d/pyarrow-19.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:f094742275586cdd6b1a03655ccff3b24b2610c3af76f810356c4c71d24a2a6c", size = 25276726 }, + { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968 }, + { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085 }, + { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613 }, + { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059 }, + { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043 }, + { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505 }, + { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641 }, ] [[package]] @@ -2992,107 +3291,136 @@ wheels = [ [[package]] name = "pyasn1-modules" -version = "0.4.1" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1d/67/6afbf0d507f73c32d21084a79946bfcfca5fbc62a72057e9c23797a737c9/pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c", size = 310028 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/89/bc88a6711935ba795a679ea6ebee07e128050d6382eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 }, + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, ] [[package]] name = "pycparser" -version = "2.22" +version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734 } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, ] [[package]] name = "pycryptodome" -version = "3.21.0" +version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/52/13b9db4a913eee948152a079fe58d035bd3d1a519584155da8e786f767e6/pycryptodome-3.21.0.tar.gz", hash = "sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297", size = 4818071 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/88/5e83de10450027c96c79dc65ac45e9d0d7a7fef334f39d3789a191f33602/pycryptodome-3.21.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:2480ec2c72438430da9f601ebc12c518c093c13111a5c1644c82cdfc2e50b1e4", size = 2495937 }, - { url = "https://files.pythonhosted.org/packages/66/e1/8f28cd8cf7f7563319819d1e172879ccce2333781ae38da61c28fe22d6ff/pycryptodome-3.21.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:de18954104667f565e2fbb4783b56667f30fb49c4d79b346f52a29cb198d5b6b", size = 1634629 }, - { url = "https://files.pythonhosted.org/packages/6a/c1/f75a1aaff0c20c11df8dc8e2bf8057e7f73296af7dfd8cbb40077d1c930d/pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de4b7263a33947ff440412339cb72b28a5a4c769b5c1ca19e33dd6cd1dcec6e", size = 2168708 }, - { url = "https://files.pythonhosted.org/packages/ea/66/6f2b7ddb457b19f73b82053ecc83ba768680609d56dd457dbc7e902c41aa/pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8", size = 2254555 }, - { url = "https://files.pythonhosted.org/packages/2c/2b/152c330732a887a86cbf591ed69bd1b489439b5464806adb270f169ec139/pycryptodome-3.21.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d85c1b613121ed3dbaa5a97369b3b757909531a959d229406a75b912dd51dd1", size = 2294143 }, - { url = "https://files.pythonhosted.org/packages/55/92/517c5c498c2980c1b6d6b9965dffbe31f3cd7f20f40d00ec4069559c5902/pycryptodome-3.21.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8898a66425a57bcf15e25fc19c12490b87bd939800f39a03ea2de2aea5e3611a", size = 2160509 }, - { url = "https://files.pythonhosted.org/packages/39/1f/c74288f54d80a20a78da87df1818c6464ac1041d10988bb7d982c4153fbc/pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_i686.whl", hash = "sha256:932c905b71a56474bff8a9c014030bc3c882cee696b448af920399f730a650c2", size = 2329480 }, - { url = "https://files.pythonhosted.org/packages/39/1b/d0b013bf7d1af7cf0a6a4fce13f5fe5813ab225313755367b36e714a63f8/pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:18caa8cfbc676eaaf28613637a89980ad2fd96e00c564135bf90bc3f0b34dd93", size = 2254397 }, - { url = "https://files.pythonhosted.org/packages/14/71/4cbd3870d3e926c34706f705d6793159ac49d9a213e3ababcdade5864663/pycryptodome-3.21.0-cp36-abi3-win32.whl", hash = "sha256:280b67d20e33bb63171d55b1067f61fbd932e0b1ad976b3a184303a3dad22764", size = 1775641 }, - { url = "https://files.pythonhosted.org/packages/43/1d/81d59d228381576b92ecede5cd7239762c14001a828bdba30d64896e9778/pycryptodome-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b7aa25fc0baa5b1d95b7633af4f5f1838467f1815442b22487426f94e0d66c53", size = 1812863 }, - { url = "https://files.pythonhosted.org/packages/25/b3/09ff7072e6d96c9939c24cf51d3c389d7c345bf675420355c22402f71b68/pycryptodome-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2cb635b67011bc147c257e61ce864879ffe6d03342dc74b6045059dfbdedafca", size = 1691593 }, - { url = "https://files.pythonhosted.org/packages/a8/91/38e43628148f68ba9b68dedbc323cf409e537fd11264031961fd7c744034/pycryptodome-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:4c26a2f0dc15f81ea3afa3b0c87b87e501f235d332b7f27e2225ecb80c0b1cdd", size = 1765997 }, - { url = "https://files.pythonhosted.org/packages/08/16/ae464d4ac338c1dd41f89c41f9488e54f7d2a3acf93bb920bb193b99f8e3/pycryptodome-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d5ebe0763c982f069d3877832254f64974139f4f9655058452603ff559c482e8", size = 1615855 }, - { url = "https://files.pythonhosted.org/packages/1e/8c/b0cee957eee1950ce7655006b26a8894cee1dc4b8747ae913684352786eb/pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee86cbde706be13f2dec5a42b52b1c1d1cbb90c8e405c68d0755134735c8dc6", size = 1650018 }, - { url = "https://files.pythonhosted.org/packages/93/4d/d7138068089b99f6b0368622e60f97a577c936d75f533552a82613060c58/pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fd54003ec3ce4e0f16c484a10bc5d8b9bd77fa662a12b85779a2d2d85d67ee0", size = 1687977 }, - { url = "https://files.pythonhosted.org/packages/96/02/90ae1ac9f28be4df0ed88c127bf4acc1b102b40053e172759d4d1c54d937/pycryptodome-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5dfafca172933506773482b0e18f0cd766fd3920bd03ec85a283df90d8a17bc6", size = 1788273 }, +sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627 }, + { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362 }, + { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625 }, + { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954 }, + { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534 }, + { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853 }, + { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465 }, + { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414 }, + { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484 }, + { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636 }, + { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675 }, + { url = "https://files.pythonhosted.org/packages/9f/7c/f5b0556590e7b4e710509105e668adb55aa9470a9f0e4dea9c40a4a11ce1/pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56", size = 1705791 }, + { url = "https://files.pythonhosted.org/packages/33/38/dcc795578d610ea1aaffef4b148b8cafcfcf4d126b1e58231ddc4e475c70/pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7", size = 1780265 }, + { url = "https://files.pythonhosted.org/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886 }, + { url = "https://files.pythonhosted.org/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151 }, + { url = "https://files.pythonhosted.org/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461 }, + { url = "https://files.pythonhosted.org/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440 }, + { url = "https://files.pythonhosted.org/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005 }, ] [[package]] name = "pydantic" -version = "2.10.6" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, ] [[package]] name = "pydantic-core" -version = "2.27.2" +version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/bc/fed5f74b5d802cf9a03e83f60f18864e90e3aed7223adaca5ffb7a8d8d64/pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa", size = 1895938 }, - { url = "https://files.pythonhosted.org/packages/71/2a/185aff24ce844e39abb8dd680f4e959f0006944f4a8a0ea372d9f9ae2e53/pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c", size = 1815684 }, - { url = "https://files.pythonhosted.org/packages/c3/43/fafabd3d94d159d4f1ed62e383e264f146a17dd4d48453319fd782e7979e/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a", size = 1829169 }, - { url = "https://files.pythonhosted.org/packages/a2/d1/f2dfe1a2a637ce6800b799aa086d079998959f6f1215eb4497966efd2274/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5", size = 1867227 }, - { url = "https://files.pythonhosted.org/packages/7d/39/e06fcbcc1c785daa3160ccf6c1c38fea31f5754b756e34b65f74e99780b5/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c", size = 2037695 }, - { url = "https://files.pythonhosted.org/packages/7a/67/61291ee98e07f0650eb756d44998214231f50751ba7e13f4f325d95249ab/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7", size = 2741662 }, - { url = "https://files.pythonhosted.org/packages/32/90/3b15e31b88ca39e9e626630b4c4a1f5a0dfd09076366f4219429e6786076/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a", size = 1993370 }, - { url = "https://files.pythonhosted.org/packages/ff/83/c06d333ee3a67e2e13e07794995c1535565132940715931c1c43bfc85b11/pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236", size = 1996813 }, - { url = "https://files.pythonhosted.org/packages/7c/f7/89be1c8deb6e22618a74f0ca0d933fdcb8baa254753b26b25ad3acff8f74/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962", size = 2005287 }, - { url = "https://files.pythonhosted.org/packages/b7/7d/8eb3e23206c00ef7feee17b83a4ffa0a623eb1a9d382e56e4aa46fd15ff2/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9", size = 2128414 }, - { url = "https://files.pythonhosted.org/packages/4e/99/fe80f3ff8dd71a3ea15763878d464476e6cb0a2db95ff1c5c554133b6b83/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af", size = 2155301 }, - { url = "https://files.pythonhosted.org/packages/2b/a3/e50460b9a5789ca1451b70d4f52546fa9e2b420ba3bfa6100105c0559238/pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4", size = 1816685 }, - { url = "https://files.pythonhosted.org/packages/57/4c/a8838731cb0f2c2a39d3535376466de6049034d7b239c0202a64aaa05533/pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31", size = 1982876 }, - { url = "https://files.pythonhosted.org/packages/46/72/af70981a341500419e67d5cb45abe552a7c74b66326ac8877588488da1ac/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e", size = 1891159 }, - { url = "https://files.pythonhosted.org/packages/ad/3d/c5913cccdef93e0a6a95c2d057d2c2cba347815c845cda79ddd3c0f5e17d/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8", size = 1768331 }, - { url = "https://files.pythonhosted.org/packages/f6/f0/a3ae8fbee269e4934f14e2e0e00928f9346c5943174f2811193113e58252/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3", size = 1822467 }, - { url = "https://files.pythonhosted.org/packages/d7/7a/7bbf241a04e9f9ea24cd5874354a83526d639b02674648af3f350554276c/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f", size = 1979797 }, - { url = "https://files.pythonhosted.org/packages/4f/5f/4784c6107731f89e0005a92ecb8a2efeafdb55eb992b8e9d0a2be5199335/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133", size = 1987839 }, - { url = "https://files.pythonhosted.org/packages/6d/a7/61246562b651dff00de86a5f01b6e4befb518df314c54dec187a78d81c84/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc", size = 1998861 }, - { url = "https://files.pythonhosted.org/packages/86/aa/837821ecf0c022bbb74ca132e117c358321e72e7f9702d1b6a03758545e2/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50", size = 2116582 }, - { url = "https://files.pythonhosted.org/packages/81/b0/5e74656e95623cbaa0a6278d16cf15e10a51f6002e3ec126541e95c29ea3/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9", size = 2151985 }, - { url = "https://files.pythonhosted.org/packages/63/37/3e32eeb2a451fddaa3898e2163746b0cffbbdbb4740d38372db0490d67f3/pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151", size = 2004715 }, +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298 }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475 }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815 }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567 }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442 }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956 }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253 }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050 }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178 }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833 }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156 }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378 }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622 }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441 }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291 }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632 }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905 }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495 }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388 }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879 }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017 }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351 }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363 }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615 }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369 }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218 }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951 }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428 }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009 }, ] [[package]] name = "pydantic-settings" -version = "2.7.1" +version = "2.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880 }, +] + +[[package]] +name = "pydeck" +version = "0.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 }, + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, ] [[package]] @@ -3110,11 +3438,23 @@ wheels = [ [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, +] + +[[package]] +name = "pympler" +version = "1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +dependencies = [ + { name = "pywin32", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/37/c384631908029676d8e7213dd956bb686af303a80db7afbc9be36bc49495/pympler-1.1.tar.gz", hash = "sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424", size = 179954 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, + { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766 }, ] [[package]] @@ -3131,47 +3471,47 @@ wheels = [ [[package]] name = "pymupdf" -version = "1.25.2" +version = "1.26.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/fc/dd8776dc5c2f8cf0e51cf81a5f1de3840996bed7ca03ec768b0733024fb9/pymupdf-1.25.2.tar.gz", hash = "sha256:9ea88ff1b3ccb359620f106a6fd5ba6877d959d21d78272052c3496ceede6eec", size = 63814915 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/a6f0e03a117fa2ad79c4b898203bb212b17804f92558a6a339298faca7bb/pymupdf-1.26.6.tar.gz", hash = "sha256:a2b4531cd4ab36d6f1f794bb6d3c33b49bda22f36d58bb1f3e81cbc10183bd2b", size = 84322494 } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/34/8c3d82719d118beb48fded78fcab7cbe9ac3bf1906dc87a9ca4fd950087d/pymupdf-1.25.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:59dea22b633cc4fc13670b4c5db50d71f8cd4f420814420f33ce47ddcb61e1f6", size = 19336722 }, - { url = "https://files.pythonhosted.org/packages/4f/ec/c7f742f56ee42be27b3afdbf3364da12f03e309f6638e666a7816d9eef23/pymupdf-1.25.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e8b8a874497cd0deee89a6a4fb76a3a08173c8d39e88fc7cf715764ec5a243e9", size = 18570847 }, - { url = "https://files.pythonhosted.org/packages/9d/27/557ee235aded5185e4824459e1540142fbb9323e1b83f77cbefe2e2c4e1e/pymupdf-1.25.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f61e5cdb25b86eb28d34aa3557b49ecf9e361d5f5cd3b1660406f8f0bf813af7", size = 19430802 }, - { url = "https://files.pythonhosted.org/packages/0e/de/35fde3d49e0d187b95ab64cc61b4d275ebc7fd4f45e152b206b0e17e6b69/pymupdf-1.25.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae8cfa7a97d78f813d286ecba32369059d88073edd1e5cf105f4cd0811f71925", size = 19994315 }, - { url = "https://files.pythonhosted.org/packages/9d/d3/a8a09b550c62306c76e1c2d892c0890287470164d7941aea35330cceee4d/pymupdf-1.25.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:295505fe1ecb7c7b57d4124d373e207ea311d8e40bc7ac3016d8ec2d60b091e9", size = 21117143 }, - { url = "https://files.pythonhosted.org/packages/ef/ac/fc4f37c7620a20d25443868ed665291e96f283eda068cda673e9edebf5f0/pymupdf-1.25.2-cp39-abi3-win32.whl", hash = "sha256:b9488c8b82bb9be36fb13ee0c8d43b0ddcc50af83b61da01e6040413d9e67da6", size = 15084555 }, - { url = "https://files.pythonhosted.org/packages/64/8e/1d0ff215b37343c7e0bec4d571f1413e4f76a416591276b97081f1814710/pymupdf-1.25.2-cp39-abi3-win_amd64.whl", hash = "sha256:1b4ca6f5780d319a08dff885a5a0e3585c5d7af04dcfa063c535b88371fd91c1", size = 16531823 }, + { url = "https://files.pythonhosted.org/packages/9e/5c/dec354eee5fe4966c715f33818ed4193e0e6c986cf8484de35b6c167fb8e/pymupdf-1.26.6-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e46f320a136ad55e5219e8f0f4061bdf3e4c12b126d2740d5a49f73fae7ea176", size = 23178988 }, + { url = "https://files.pythonhosted.org/packages/ec/a0/11adb742d18142bd623556cd3b5d64649816decc5eafd30efc9498657e76/pymupdf-1.26.6-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:6844cd2396553c0fa06de4869d5d5ecb1260e6fc3b9d85abe8fa35f14dd9d688", size = 22469764 }, + { url = "https://files.pythonhosted.org/packages/e4/c8/377cf20e31f58d4c243bfcf2d3cb7466d5b97003b10b9f1161f11eb4a994/pymupdf-1.26.6-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:617ba69e02c44f0da1c0e039ea4a26cf630849fd570e169c71daeb8ac52a81d6", size = 23502227 }, + { url = "https://files.pythonhosted.org/packages/4f/bf/6e02e3d84b32c137c71a0a3dcdba8f2f6e9950619a3bc272245c7c06a051/pymupdf-1.26.6-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7777d0b7124c2ebc94849536b6a1fb85d158df3b9d873935e63036559391534c", size = 24115381 }, + { url = "https://files.pythonhosted.org/packages/ab/9d/30f7fcb3776bfedde66c06297960debe4883b1667294a1ee9426c942e94d/pymupdf-1.26.6-cp310-abi3-win32.whl", hash = "sha256:8f3ef05befc90ca6bb0f12983200a7048d5bff3e1c1edef1bb3de60b32cb5274", size = 17203613 }, + { url = "https://files.pythonhosted.org/packages/f9/e8/989f4eaa369c7166dc24f0eaa3023f13788c40ff1b96701f7047421554a8/pymupdf-1.26.6-cp310-abi3-win_amd64.whl", hash = "sha256:ce02ca96ed0d1acfd00331a4d41a34c98584d034155b06fd4ec0f051718de7ba", size = 18405680 }, ] [[package]] name = "pymupdf4llm" -version = "0.0.17" +version = "0.2.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pymupdf" }, + { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/3c/1a530a410bdf76d83289bf30b3b86236d338b3f5f21842790c2cf7e9c1f6/pymupdf4llm-0.0.17.tar.gz", hash = "sha256:27287ef9fe0217cf37841a3ef2bcf70da2553c43d95ea39b664a6de6485678c3", size = 25180 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/bd/5c1958ac1ab68c12a09aef1128b3522e1ada6086c6cb42f880dcb294b13e/pymupdf4llm-0.2.6.tar.gz", hash = "sha256:e2deb44ba35aef92c4d84254ef70739feb982256235de2740ffb016b25480da4", size = 64787 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/af/1576ecfc8a62d31c0c8b34b856e52f6b05f1d76546dbac0e1d037f044a9e/pymupdf4llm-0.0.17-py3-none-any.whl", hash = "sha256:26de9996945f15e3ca507908f80dc18a959f5b5214bb2e302c7f7034089665a0", size = 26190 }, + { url = "https://files.pythonhosted.org/packages/b8/fa/2c7d7ce85d7844097d96a8085e719ef5508eaf60e0d0ec24d2d3d42fffc9/pymupdf4llm-0.2.6-py3-none-any.whl", hash = "sha256:0b8f19d867ca3d9f1e0835a376431b74ef799a0bc0bae1442d1d429fde2d355b", size = 66562 }, ] [[package]] name = "pypandoc" -version = "1.15" +version = "1.16.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/88/26e650d053df5f3874aa3c05901a14166ce3271f58bfe114fd776987efbd/pypandoc-1.15.tar.gz", hash = "sha256:ea25beebe712ae41d63f7410c08741a3cab0e420f6703f95bc9b3a749192ce13", size = 32940 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/06/0763e0ccc81754d3eadb21b2cb86cf21bdedc9b52698c2ad6785db7f0a4e/pypandoc-1.15-py3-none-any.whl", hash = "sha256:4ededcc76c8770f27aaca6dff47724578428eca84212a31479403a9731fc2b16", size = 21321 }, + { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451 }, ] [[package]] name = "pyparsing" -version = "3.2.1" +version = "3.2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 }, + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890 }, ] [[package]] @@ -3210,20 +3550,20 @@ wheels = [ [[package]] name = "python-dotenv" -version = "1.0.1" +version = "1.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, ] [[package]] name = "python-json-logger" -version = "3.2.1" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/c4/358cd13daa1d912ef795010897a483ab2f0b41c9ea1b35235a8b2f7d15a7/python_json_logger-3.2.1.tar.gz", hash = "sha256:8eb0554ea17cb75b05d2848bc14fb02fbdbd9d6972120781b974380bfa162008", size = 16287 } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/72/2f30cf26664fcfa0bd8ec5ee62ec90c03bd485e4a294d92aabc76c5203a5/python_json_logger-3.2.1-py3-none-any.whl", hash = "sha256:cdc17047eb5374bd311e748b42f99d71223f3b0e186f4206cc5d52aefe85b090", size = 14924 }, + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548 }, ] [[package]] @@ -3285,147 +3625,144 @@ wheels = [ [[package]] name = "pytz" -version = "2024.2" +version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, ] [[package]] name = "pywin32" -version = "308" +version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/a6/3e9f2c474895c1bb61b11fa9640be00067b5c5b363c501ee9c3fa53aec01/pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e", size = 5927028 }, - { url = "https://files.pythonhosted.org/packages/d9/b4/84e2463422f869b4b718f79eb7530a4c1693e96b8a4e5e968de38be4d2ba/pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e", size = 6558484 }, - { url = "https://files.pythonhosted.org/packages/9f/8f/fb84ab789713f7c6feacaa08dad3ec8105b88ade8d1c4f0f0dfcaaa017d6/pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c", size = 7971454 }, + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432 }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103 }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557 }, ] [[package]] name = "pywinpty" -version = "2.0.14" +version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/82/90f8750423cba4b9b6c842df227609fb60704482d7abf6dd47e2babc055a/pywinpty-2.0.14.tar.gz", hash = "sha256:18bd9529e4a5daf2d9719aa17788ba6013e594ae94c5a0c27e83df3278b0660e", size = 27769 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/09/56376af256eab8cc5f8982a3b138d387136eca27fa1a8a68660e8ed59e4b/pywinpty-2.0.14-cp310-none-win_amd64.whl", hash = "sha256:0b149c2918c7974f575ba79f5a4aad58bd859a52fa9eb1296cc22aa412aa411f", size = 1397115 }, + { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330 }, ] [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227 }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019 }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646 }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793 }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293 }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872 }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828 }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415 }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561 }, ] [[package]] name = "pyzmq" -version = "26.2.0" +version = "27.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/05/bed626b9f7bb2322cdbbf7b4bd8f54b1b617b0d2ab2d3547d6e39428a48e/pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f", size = 271975 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/a8/9837c39aba390eb7d01924ace49d761c8dbe7bc2d6082346d00c8332e431/pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629", size = 1340058 }, - { url = "https://files.pythonhosted.org/packages/a2/1f/a006f2e8e4f7d41d464272012695da17fb95f33b54342612a6890da96ff6/pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b", size = 1008818 }, - { url = "https://files.pythonhosted.org/packages/b6/09/b51b6683fde5ca04593a57bbe81788b6b43114d8f8ee4e80afc991e14760/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764", size = 673199 }, - { url = "https://files.pythonhosted.org/packages/c9/78/486f3e2e824f3a645238332bf5a4c4b4477c3063033a27c1e4052358dee2/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c", size = 911762 }, - { url = "https://files.pythonhosted.org/packages/5e/3b/2eb1667c9b866f53e76ee8b0c301b0469745a23bd5a87b7ee3d5dd9eb6e5/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a", size = 868773 }, - { url = "https://files.pythonhosted.org/packages/16/29/ca99b4598a9dc7e468b5417eda91f372b595be1e3eec9b7cbe8e5d3584e8/pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88", size = 868834 }, - { url = "https://files.pythonhosted.org/packages/ad/e5/9efaeb1d2f4f8c50da04144f639b042bc52869d3a206d6bf672ab3522163/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f", size = 1202861 }, - { url = "https://files.pythonhosted.org/packages/c3/62/c721b5608a8ac0a69bb83cbb7d07a56f3ff00b3991a138e44198a16f94c7/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282", size = 1515304 }, - { url = "https://files.pythonhosted.org/packages/87/84/e8bd321aa99b72f48d4606fc5a0a920154125bd0a4608c67eab742dab087/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea", size = 1414712 }, - { url = "https://files.pythonhosted.org/packages/cd/cd/420e3fd1ac6977b008b72e7ad2dae6350cc84d4c5027fc390b024e61738f/pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2", size = 578113 }, - { url = "https://files.pythonhosted.org/packages/5c/57/73930d56ed45ae0cb4946f383f985c855c9b3d4063f26416998f07523c0e/pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971", size = 641631 }, - { url = "https://files.pythonhosted.org/packages/61/d2/ae6ac5c397f1ccad59031c64beaafce7a0d6182e0452cc48f1c9c87d2dd0/pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa", size = 543528 }, - { url = "https://files.pythonhosted.org/packages/53/fb/36b2b2548286e9444e52fcd198760af99fd89102b5be50f0660fcfe902df/pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072", size = 906955 }, - { url = "https://files.pythonhosted.org/packages/77/8f/6ce54f8979a01656e894946db6299e2273fcee21c8e5fa57c6295ef11f57/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1", size = 565701 }, - { url = "https://files.pythonhosted.org/packages/ee/1c/bf8cd66730a866b16db8483286078892b7f6536f8c389fb46e4beba0a970/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d", size = 794312 }, - { url = "https://files.pythonhosted.org/packages/71/43/91fa4ff25bbfdc914ab6bafa0f03241d69370ef31a761d16bb859f346582/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca", size = 752775 }, - { url = "https://files.pythonhosted.org/packages/ec/d2/3b2ab40f455a256cb6672186bea95cd97b459ce4594050132d71e76f0d6f/pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c", size = 550762 }, +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850 }, + { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380 }, + { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421 }, + { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149 }, + { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070 }, + { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441 }, + { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529 }, + { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276 }, + { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208 }, + { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766 }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, + { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266 }, + { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206 }, + { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747 }, + { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371 }, + { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862 }, ] [[package]] name = "rapidfuzz" -version = "3.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a4/aa/25e5a20131369d82c7b8288c99c2c3011ec47a3f5953ccc9cb8145720be5/rapidfuzz-3.11.0.tar.gz", hash = "sha256:a53ca4d3f52f00b393fab9b5913c5bafb9afc27d030c8a1db1283da6917a860f", size = 57983000 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/70/820ebf9eb22ad97b9e0bb9fd1ad8c6be4c8db5a0974d12ce27b5c9a30db0/rapidfuzz-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb8a54543d16ab1b69e2c5ed96cabbff16db044a50eddfc028000138ca9ddf33", size = 1954240 }, - { url = "https://files.pythonhosted.org/packages/41/bc/e39abdc28160d8147ccab0aa922a29be50529dcf149615a68a324ff6f9b1/rapidfuzz-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:231c8b2efbd7f8d2ecd1ae900363ba168b8870644bb8f2b5aa96e4a7573bde19", size = 1427139 }, - { url = "https://files.pythonhosted.org/packages/b6/2d/19b8e5d80257b13d73ba994552b78a69ac2ed70f1de716f1b02fcb84d09c/rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54e7f442fb9cca81e9df32333fb075ef729052bcabe05b0afc0441f462299114", size = 1419602 }, - { url = "https://files.pythonhosted.org/packages/8c/82/1fc80cc531ec712872025c19118d78eb23aff09c7144b380c2c4b544b0d1/rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:906f1f2a1b91c06599b3dd1be207449c5d4fc7bd1e1fa2f6aef161ea6223f165", size = 5635370 }, - { url = "https://files.pythonhosted.org/packages/3c/5c/007b90af25f98e301b5f7a095059b09f602701443d555724c9226a45514c/rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed59044aea9eb6c663112170f2399b040d5d7b162828b141f2673e822093fa8", size = 1680848 }, - { url = "https://files.pythonhosted.org/packages/01/04/e481530eff5d1cf337b86a3095dd4de0b758c37291e51eb0d8c4f7d49719/rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cb1965a28b0fa64abdee130c788a0bc0bb3cf9ef7e3a70bf055c086c14a3d7e", size = 1682131 }, - { url = "https://files.pythonhosted.org/packages/10/15/b0ec18edfe6146d8915679644ab7584cd0165724d6a53bcc43bd59f8edb5/rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b488b244931d0291412917e6e46ee9f6a14376625e150056fe7c4426ef28225", size = 3134097 }, - { url = "https://files.pythonhosted.org/packages/8b/0e/cf0a5d62977381bca981fc171fd6c85dc52ca1239eaacf9c1d38978c5866/rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f0ba13557fec9d5ffc0a22826754a7457cc77f1b25145be10b7bb1d143ce84c6", size = 2332928 }, - { url = "https://files.pythonhosted.org/packages/dc/71/568d383eb36586c9e7e13f1327203e2be0938e5ff070c1fa2a99b418808e/rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3871fa7dfcef00bad3c7e8ae8d8fd58089bad6fb21f608d2bf42832267ca9663", size = 6940409 }, - { url = "https://files.pythonhosted.org/packages/ba/23/02972657d69e6d3aae2cdbd67debad080410ff9ef8849d8eab5e580a48a5/rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b2669eafee38c5884a6e7cc9769d25c19428549dcdf57de8541cf9e82822e7db", size = 2715928 }, - { url = "https://files.pythonhosted.org/packages/17/17/d964d770faa4e25e125618c00e31607cf8ce639d518fc29d200edf06cfda/rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ffa1bb0e26297b0f22881b219ffc82a33a3c84ce6174a9d69406239b14575bd5", size = 3265078 }, - { url = "https://files.pythonhosted.org/packages/bc/13/a117412b1e4ed0bb23b9891a45a59812d96fde8c076b8b8b828aa7ca3710/rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:45b15b8a118856ac9caac6877f70f38b8a0d310475d50bc814698659eabc1cdb", size = 4169215 }, - { url = "https://files.pythonhosted.org/packages/9f/0d/89ef496aedf885db4bfe7f46ac6727666afe0d9d8ca5b4f9c7cc8eef0378/rapidfuzz-3.11.0-cp310-cp310-win32.whl", hash = "sha256:22033677982b9c4c49676f215b794b0404073f8974f98739cb7234e4a9ade9ad", size = 1841736 }, - { url = "https://files.pythonhosted.org/packages/47/9a/69019f4e9c8a42e4aca0169dbae71602aba4e0fa4c5e84515f3ed682e59a/rapidfuzz-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:be15496e7244361ff0efcd86e52559bacda9cd975eccf19426a0025f9547c792", size = 1614955 }, - { url = "https://files.pythonhosted.org/packages/37/65/6fb036e39d175299ce44e5186ee2d08b9ea02d732ed6dbd70280f63b4eba/rapidfuzz-3.11.0-cp310-cp310-win_arm64.whl", hash = "sha256:714a7ba31ba46b64d30fccfe95f8013ea41a2e6237ba11a805a27cdd3bce2573", size = 863543 }, - { url = "https://files.pythonhosted.org/packages/30/5a/8ac67667663d24cc4d4b76f63783e58ef03e4d4843d02dab6b2f8470ea5e/rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f06e3c4c0a8badfc4910b9fd15beb1ad8f3b8fafa8ea82c023e5e607b66a78e4", size = 1853100 }, - { url = "https://files.pythonhosted.org/packages/dc/72/b043c26e93fb1bc5dfab1e5dd0f8d2f6135c2aa48e6db0660d4ecc5b157a/rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fe7aaf5a54821d340d21412f7f6e6272a9b17a0cbafc1d68f77f2fc11009dcd5", size = 1361961 }, - { url = "https://files.pythonhosted.org/packages/5c/4a/29916c0dd853d22ef7b988af43f4e34d327581e16f60b4c9b0f229fa306c/rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25398d9ac7294e99876a3027ffc52c6bebeb2d702b1895af6ae9c541ee676702", size = 1354313 }, - { url = "https://files.pythonhosted.org/packages/41/39/f352af4ede7faeeea20bae2537f1fa60c3bbbf2696f0f2f3dda696745239/rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a52eea839e4bdc72c5e60a444d26004da00bb5bc6301e99b3dde18212e41465", size = 5478019 }, - { url = "https://files.pythonhosted.org/packages/99/8e/86f8a11ac0edda63ff5314d992aa1576fff5d8233f4310d46a6bb0551122/rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c87319b0ab9d269ab84f6453601fd49b35d9e4a601bbaef43743f26fabf496c", size = 3056881 }, - { url = "https://files.pythonhosted.org/packages/98/53/222dceb24a83c7d7d76086b6d5bfd3d6aa9988ea73d356d287b5c437c0d5/rapidfuzz-3.11.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3048c6ed29d693fba7d2a7caf165f5e0bb2b9743a0989012a98a47b975355cca", size = 1543944 }, +version = "3.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376 }, + { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903 }, + { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655 }, + { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708 }, + { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106 }, + { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048 }, + { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020 }, + { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958 }, + { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043 }, + { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273 }, + { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875 }, ] [[package]] name = "referencing" -version = "0.36.2" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, ] [[package]] name = "regex" -version = "2024.11.6" +version = "2025.11.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, - { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, - { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, - { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, - { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, - { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, - { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, - { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, - { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, - { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, - { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, - { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, - { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, - { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, - { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, - { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, + { url = "https://files.pythonhosted.org/packages/8a/d6/d788d52da01280a30a3f6268aef2aa71043bff359c618fea4c5b536654d5/regex-2025.11.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2b441a4ae2c8049106e8b39973bfbddfb25a179dda2bdb99b0eeb60c40a6a3af", size = 488087 }, + { url = "https://files.pythonhosted.org/packages/69/39/abec3bd688ec9bbea3562de0fd764ff802976185f5ff22807bf0a2697992/regex-2025.11.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2fa2eed3f76677777345d2f81ee89f5de2f5745910e805f7af7386a920fa7313", size = 290544 }, + { url = "https://files.pythonhosted.org/packages/39/b3/9a231475d5653e60002508f41205c61684bb2ffbf2401351ae2186897fc4/regex-2025.11.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8b4a27eebd684319bdf473d39f1d79eed36bf2cd34bd4465cdb4618d82b3d56", size = 288408 }, + { url = "https://files.pythonhosted.org/packages/c3/c5/1929a0491bd5ac2d1539a866768b88965fa8c405f3e16a8cef84313098d6/regex-2025.11.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cf77eac15bd264986c4a2c63353212c095b40f3affb2bc6b4ef80c4776c1a28", size = 781584 }, + { url = "https://files.pythonhosted.org/packages/ce/fd/16aa16cf5d497ef727ec966f74164fbe75d6516d3d58ac9aa989bc9cdaad/regex-2025.11.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b7f9ee819f94c6abfa56ec7b1dbab586f41ebbdc0a57e6524bd5e7f487a878c7", size = 850733 }, + { url = "https://files.pythonhosted.org/packages/e6/49/3294b988855a221cb6565189edf5dc43239957427df2d81d4a6b15244f64/regex-2025.11.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:838441333bc90b829406d4a03cb4b8bf7656231b84358628b0406d803931ef32", size = 898691 }, + { url = "https://files.pythonhosted.org/packages/14/62/b56d29e70b03666193369bdbdedfdc23946dbe9f81dd78ce262c74d988ab/regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe6d3f0c9e3b7e8c0c694b24d25e677776f5ca26dce46fd6b0489f9c8339391", size = 791662 }, + { url = "https://files.pythonhosted.org/packages/15/fc/e4c31d061eced63fbf1ce9d853975f912c61a7d406ea14eda2dd355f48e7/regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2ab815eb8a96379a27c3b6157fcb127c8f59c36f043c1678110cea492868f1d5", size = 782587 }, + { url = "https://files.pythonhosted.org/packages/b2/bb/5e30c7394bcf63f0537121c23e796be67b55a8847c3956ae6068f4c70702/regex-2025.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:728a9d2d173a65b62bdc380b7932dd8e74ed4295279a8fe1021204ce210803e7", size = 774709 }, + { url = "https://files.pythonhosted.org/packages/c5/c4/fce773710af81b0cb37cb4ff0947e75d5d17dee304b93d940b87a67fc2f4/regex-2025.11.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:509dc827f89c15c66a0c216331260d777dd6c81e9a4e4f830e662b0bb296c313", size = 845773 }, + { url = "https://files.pythonhosted.org/packages/7b/5e/9466a7ec4b8ec282077095c6eb50a12a389d2e036581134d4919e8ca518c/regex-2025.11.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:849202cd789e5f3cf5dcc7822c34b502181b4824a65ff20ce82da5524e45e8e9", size = 836164 }, + { url = "https://files.pythonhosted.org/packages/95/18/82980a60e8ed1594eb3c89eb814fb276ef51b9af7caeab1340bfd8564af6/regex-2025.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b6f78f98741dcc89607c16b1e9426ee46ce4bf31ac5e6b0d40e81c89f3481ea5", size = 779832 }, + { url = "https://files.pythonhosted.org/packages/03/cc/90ab0fdbe6dce064a42015433f9152710139fb04a8b81b4fb57a1cb63ffa/regex-2025.11.3-cp310-cp310-win32.whl", hash = "sha256:149eb0bba95231fb4f6d37c8f760ec9fa6fabf65bab555e128dde5f2475193ec", size = 265802 }, + { url = "https://files.pythonhosted.org/packages/34/9d/e9e8493a85f3b1ddc4a5014465f5c2b78c3ea1cbf238dcfde78956378041/regex-2025.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:ee3a83ce492074c35a74cc76cf8235d49e77b757193a5365ff86e3f2f93db9fd", size = 277722 }, + { url = "https://files.pythonhosted.org/packages/15/c4/b54b24f553966564506dbf873a3e080aef47b356a3b39b5d5aba992b50db/regex-2025.11.3-cp310-cp310-win_arm64.whl", hash = "sha256:38af559ad934a7b35147716655d4a2f79fcef2d695ddfe06a06ba40ae631fa7e", size = 270289 }, ] [[package]] name = "requests" -version = "2.32.3" +version = "2.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -3433,9 +3770,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, ] [package.optional-dependencies] @@ -3477,116 +3814,154 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, ] +[[package]] +name = "rfc3987-syntax" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lark" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, +] + [[package]] name = "rich" -version = "13.9.4" +version = "14.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990 } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393 }, ] [[package]] name = "rich-toolkit" -version = "0.13.2" +version = "0.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/8a/71cfbf6bf6257ea785d1f030c22468f763eea1b3e5417620f2ba9abd6dca/rich_toolkit-0.13.2.tar.gz", hash = "sha256:fea92557530de7c28f121cbed572ad93d9e0ddc60c3ca643f1b831f2f56b95d3", size = 72288 } +sdist = { url = "https://files.pythonhosted.org/packages/ad/d0/8f8de36e1abf8339b497ce700dd7251ca465ffca4a1976969b0eaeb596fb/rich_toolkit-0.17.0.tar.gz", hash = "sha256:17ca7a32e613001aa0945ddea27a246f6de01dfc4c12403254c057a8ee542977", size = 187955 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/1b/1c2f43af46456050b27810a7a013af8a7e12bc545a0cdc00eb0df55eb769/rich_toolkit-0.13.2-py3-none-any.whl", hash = "sha256:f3f6c583e5283298a2f7dbd3c65aca18b7f818ad96174113ab5bec0b0e35ed61", size = 13566 }, + { url = "https://files.pythonhosted.org/packages/b2/42/ef2ed40699567661d03b0b511ac46cf6cee736de8f3666819c12d6d20696/rich_toolkit-0.17.0-py3-none-any.whl", hash = "sha256:06fb47a5c5259d6b480287cd38aff5f551b6e1a307f90ed592453dd360e4e71e", size = 31412 }, +] + +[[package]] +name = "rignore" +version = "0.7.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999 }, + { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824 }, + { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121 }, + { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813 }, + { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019 }, + { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822 }, + { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820 }, + { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050 }, + { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164 }, + { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028 }, + { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024 }, + { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531 }, + { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817 }, + { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001 }, + { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462 }, + { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918 }, + { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922 }, + { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987 }, + { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110 }, + { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339 }, + { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680 }, + { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045 }, + { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310 }, + { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998 }, + { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178 }, + { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190 }, ] [[package]] name = "rpds-py" -version = "0.22.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/80/cce854d0921ff2f0a9fa831ba3ad3c65cee3a46711addf39a2af52df2cfd/rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", size = 26771 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/2a/ead1d09e57449b99dcc190d8d2323e3a167421d8f8fdf0f217c6f6befe47/rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967", size = 359514 }, - { url = "https://files.pythonhosted.org/packages/8f/7e/1254f406b7793b586c68e217a6a24ec79040f85e030fff7e9049069284f4/rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37", size = 349031 }, - { url = "https://files.pythonhosted.org/packages/aa/da/17c6a2c73730d426df53675ff9cc6653ac7a60b6438d03c18e1c822a576a/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24", size = 381485 }, - { url = "https://files.pythonhosted.org/packages/aa/13/2dbacd820466aa2a3c4b747afb18d71209523d353cf865bf8f4796c969ea/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff", size = 386794 }, - { url = "https://files.pythonhosted.org/packages/6d/62/96905d0a35ad4e4bc3c098b2f34b2e7266e211d08635baa690643d2227be/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c", size = 423523 }, - { url = "https://files.pythonhosted.org/packages/eb/1b/d12770f2b6a9fc2c3ec0d810d7d440f6d465ccd8b7f16ae5385952c28b89/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e", size = 446695 }, - { url = "https://files.pythonhosted.org/packages/4d/cf/96f1fd75512a017f8e07408b6d5dbeb492d9ed46bfe0555544294f3681b3/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec", size = 381959 }, - { url = "https://files.pythonhosted.org/packages/ab/f0/d1c5b501c8aea85aeb938b555bfdf7612110a2f8cdc21ae0482c93dd0c24/rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c", size = 410420 }, - { url = "https://files.pythonhosted.org/packages/33/3b/45b6c58fb6aad5a569ae40fb890fc494c6b02203505a5008ee6dc68e65f7/rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09", size = 557620 }, - { url = "https://files.pythonhosted.org/packages/83/62/3fdd2d3d47bf0bb9b931c4c73036b4ab3ec77b25e016ae26fab0f02be2af/rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00", size = 584202 }, - { url = "https://files.pythonhosted.org/packages/04/f2/5dced98b64874b84ca824292f9cee2e3f30f3bcf231d15a903126684f74d/rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf", size = 552787 }, - { url = "https://files.pythonhosted.org/packages/67/13/2273dea1204eda0aea0ef55145da96a9aa28b3f88bb5c70e994f69eda7c3/rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652", size = 220088 }, - { url = "https://files.pythonhosted.org/packages/4e/80/8c8176b67ad7f4a894967a7a4014ba039626d96f1d4874d53e409b58d69f/rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8", size = 231737 }, - { url = "https://files.pythonhosted.org/packages/8b/63/e29f8ee14fcf383574f73b6bbdcbec0fbc2e5fc36b4de44d1ac389b1de62/rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d", size = 360786 }, - { url = "https://files.pythonhosted.org/packages/d3/e0/771ee28b02a24e81c8c0e645796a371350a2bb6672753144f36ae2d2afc9/rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd", size = 350589 }, - { url = "https://files.pythonhosted.org/packages/cf/49/abad4c4a1e6f3adf04785a99c247bfabe55ed868133e2d1881200aa5d381/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493", size = 381848 }, - { url = "https://files.pythonhosted.org/packages/3a/7d/f4bc6d6fbe6af7a0d2b5f2ee77079efef7c8528712745659ec0026888998/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96", size = 387879 }, - { url = "https://files.pythonhosted.org/packages/13/b0/575c797377fdcd26cedbb00a3324232e4cb2c5d121f6e4b0dbf8468b12ef/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123", size = 423916 }, - { url = "https://files.pythonhosted.org/packages/54/78/87157fa39d58f32a68d3326f8a81ad8fb99f49fe2aa7ad9a1b7d544f9478/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad", size = 448410 }, - { url = "https://files.pythonhosted.org/packages/59/69/860f89996065a88be1b6ff2d60e96a02b920a262d8aadab99e7903986597/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9", size = 382841 }, - { url = "https://files.pythonhosted.org/packages/bd/d7/bc144e10d27e3cb350f98df2492a319edd3caaf52ddfe1293f37a9afbfd7/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e", size = 409662 }, - { url = "https://files.pythonhosted.org/packages/14/2a/6bed0b05233c291a94c7e89bc76ffa1c619d4e1979fbfe5d96024020c1fb/rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338", size = 558221 }, - { url = "https://files.pythonhosted.org/packages/11/23/cd8f566de444a137bc1ee5795e47069a947e60810ba4152886fe5308e1b7/rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566", size = 583780 }, - { url = "https://files.pythonhosted.org/packages/8d/63/79c3602afd14d501f751e615a74a59040328da5ef29ed5754ae80d236b84/rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe", size = 553619 }, - { url = "https://files.pythonhosted.org/packages/9f/2e/c5c1689e80298d4e94c75b70faada4c25445739d91b94c211244a3ed7ed1/rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d", size = 233338 }, +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490 }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751 }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696 }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136 }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699 }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022 }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522 }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579 }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305 }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503 }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322 }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792 }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901 }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823 }, ] [[package]] name = "rsa" -version = "4.9" +version = "4.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 } +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 }, + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 }, ] [[package]] name = "s3transfer" -version = "0.11.2" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/45/2323b5928f86fd29f9afdcef4659f68fa73eaa5356912b774227f5cf46b5/s3transfer-0.11.2.tar.gz", hash = "sha256:3b39185cb72f5acc77db1a58b6e25b977f28d20496b6e58d6813d75f464d632f", size = 147885 } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/ac/e7dc469e49048dc57f62e0c555d2ee3117fa30813d2a1a2962cce3a2a82a/s3transfer-0.11.2-py3-none-any.whl", hash = "sha256:be6ecb39fadd986ef1701097771f87e4d2f821f27f6071c872143884d2950fbc", size = 84151 }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830 }, ] [[package]] name = "safetensors" -version = "0.5.2" +version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/4f/2ef9ef1766f8c194b01b67a63a444d2e557c8fe1d82faf3ebd85f370a917/safetensors-0.5.2.tar.gz", hash = "sha256:cb4a8d98ba12fa016f4241932b1fc5e702e5143f5374bba0bbcf7ddc1c4cf2b8", size = 66957 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/d1/017e31e75e274492a11a456a9e7c171f8f7911fe50735b4ec6ff37221220/safetensors-0.5.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:45b6092997ceb8aa3801693781a71a99909ab9cc776fbc3fa9322d29b1d3bef2", size = 427067 }, - { url = "https://files.pythonhosted.org/packages/24/84/e9d3ff57ae50dd0028f301c9ee064e5087fe8b00e55696677a0413c377a7/safetensors-0.5.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6d0d6a8ee2215a440e1296b843edf44fd377b055ba350eaba74655a2fe2c4bae", size = 408856 }, - { url = "https://files.pythonhosted.org/packages/f1/1d/fe95f5dd73db16757b11915e8a5106337663182d0381811c81993e0014a9/safetensors-0.5.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86016d40bcaa3bcc9a56cd74d97e654b5f4f4abe42b038c71e4f00a089c4526c", size = 450088 }, - { url = "https://files.pythonhosted.org/packages/cf/21/e527961b12d5ab528c6e47b92d5f57f33563c28a972750b238b871924e49/safetensors-0.5.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:990833f70a5f9c7d3fc82c94507f03179930ff7d00941c287f73b6fcbf67f19e", size = 458966 }, - { url = "https://files.pythonhosted.org/packages/a5/8b/1a037d7a57f86837c0b41905040369aea7d8ca1ec4b2a77592372b2ec380/safetensors-0.5.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dfa7c2f3fe55db34eba90c29df94bcdac4821043fc391cb5d082d9922013869", size = 509915 }, - { url = "https://files.pythonhosted.org/packages/61/3d/03dd5cfd33839df0ee3f4581a20bd09c40246d169c0e4518f20b21d5f077/safetensors-0.5.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46ff2116150ae70a4e9c490d2ab6b6e1b1b93f25e520e540abe1b81b48560c3a", size = 527664 }, - { url = "https://files.pythonhosted.org/packages/c5/dc/8952caafa9a10a3c0f40fa86bacf3190ae7f55fa5eef87415b97b29cb97f/safetensors-0.5.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab696dfdc060caffb61dbe4066b86419107a24c804a4e373ba59be699ebd8d5", size = 461978 }, - { url = "https://files.pythonhosted.org/packages/60/da/82de1fcf1194e3dbefd4faa92dc98b33c06bed5d67890e0962dd98e18287/safetensors-0.5.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03c937100f38c9ff4c1507abea9928a6a9b02c9c1c9c3609ed4fb2bf413d4975", size = 491253 }, - { url = "https://files.pythonhosted.org/packages/5a/9a/d90e273c25f90c3ba1b0196a972003786f04c39e302fbd6649325b1272bb/safetensors-0.5.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a00e737948791b94dad83cf0eafc09a02c4d8c2171a239e8c8572fe04e25960e", size = 628644 }, - { url = "https://files.pythonhosted.org/packages/70/3c/acb23e05aa34b4f5edd2e7f393f8e6480fbccd10601ab42cd03a57d4ab5f/safetensors-0.5.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:d3a06fae62418ec8e5c635b61a8086032c9e281f16c63c3af46a6efbab33156f", size = 721648 }, - { url = "https://files.pythonhosted.org/packages/71/45/eaa3dba5253a7c6931230dc961641455710ab231f8a89cb3c4c2af70f8c8/safetensors-0.5.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1506e4c2eda1431099cebe9abf6c76853e95d0b7a95addceaa74c6019c65d8cf", size = 659588 }, - { url = "https://files.pythonhosted.org/packages/b0/71/2f9851164f821064d43b481ddbea0149c2d676c4f4e077b178e7eeaa6660/safetensors-0.5.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5c5b5d9da594f638a259fca766046f44c97244cc7ab8bef161b3e80d04becc76", size = 632533 }, - { url = "https://files.pythonhosted.org/packages/00/f1/5680e2ef61d9c61454fad82c344f0e40b8741a9dbd1e31484f0d31a9b1c3/safetensors-0.5.2-cp38-abi3-win32.whl", hash = "sha256:fe55c039d97090d1f85277d402954dd6ad27f63034fa81985a9cc59655ac3ee2", size = 291167 }, - { url = "https://files.pythonhosted.org/packages/86/ca/aa489392ec6fb59223ffce825461e1f811a3affd417121a2088be7a5758b/safetensors-0.5.2-cp38-abi3-win_amd64.whl", hash = "sha256:78abdddd03a406646107f973c7843276e7b64e5e32623529dc17f3d94a20f589", size = 303756 }, +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430 }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977 }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890 }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885 }, ] [[package]] name = "scikit-learn" -version = "1.5.2" +version = "1.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "joblib" }, @@ -3594,13 +3969,13 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/59/44985a2bdc95c74e34fef3d10cb5d93ce13b0e2a7baefffe1b53853b502d/scikit_learn-1.5.2.tar.gz", hash = "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d", size = 7001680 } +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136 } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/89/be41419b4bec629a4691183a5eb1796f91252a13a5ffa243fd958cad7e91/scikit_learn-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:299406827fb9a4f862626d0fe6c122f5f87f8910b86fe5daa4c32dcd742139b6", size = 12106070 }, - { url = "https://files.pythonhosted.org/packages/bf/e0/3b6d777d375f3b685f433c93384cdb724fb078e1dc8f8ff0950467e56c30/scikit_learn-1.5.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:2d4cad1119c77930b235579ad0dc25e65c917e756fe80cab96aa3b9428bd3fb0", size = 10971758 }, - { url = "https://files.pythonhosted.org/packages/7b/31/eb7dd56c371640753953277de11356c46a3149bfeebb3d7dcd90b993715a/scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c412ccc2ad9bf3755915e3908e677b367ebc8d010acbb3f182814524f2e5540", size = 12500080 }, - { url = "https://files.pythonhosted.org/packages/4c/1e/a7c7357e704459c7d56a18df4a0bf08669442d1f8878cc0864beccd6306a/scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a686885a4b3818d9e62904d91b57fa757fc2bed3e465c8b177be652f4dd37c8", size = 13347241 }, - { url = "https://files.pythonhosted.org/packages/48/76/154ebda6794faf0b0f3ccb1b5cd9a19f0a63cb9e1f3d2c61b6114002677b/scikit_learn-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:c15b1ca23d7c5f33cc2cb0a0d6aaacf893792271cddff0edbd6a40e8319bc113", size = 11000477 }, + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221 }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834 }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938 }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818 }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969 }, ] [[package]] @@ -3666,6 +4041,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", size = 18072 }, ] +[[package]] +name = "sentence-transformers" +version = "5.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "pillow" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/96/f3f3409179d14dbfdbea8622e2e9eaa3c8836ddcaecd2cd5ff0a11731d20/sentence_transformers-5.1.2.tar.gz", hash = "sha256:0f6c8bd916a78dc65b366feb8d22fd885efdb37432e7630020d113233af2b856", size = 375185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/a6/a607a737dc1a00b7afe267b9bfde101b8cee2529e197e57471d23137d4e5/sentence_transformers-5.1.2-py3-none-any.whl", hash = "sha256:724ce0ea62200f413f1a5059712aff66495bc4e815a1493f7f9bca242414c333", size = 488009 }, +] + [[package]] name = "sentencepiece" version = "0.2.0" @@ -3682,13 +4076,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525 }, ] +[[package]] +name = "sentry-sdk" +version = "2.47.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/2a/d225cbf87b6c8ecce5664db7bcecb82c317e448e3b24a2dcdaacb18ca9a7/sentry_sdk-2.47.0.tar.gz", hash = "sha256:8218891d5e41b4ea8d61d2aed62ed10c80e39d9f2959d6f939efbf056857e050", size = 381895 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/ac/d6286ea0d49e7b58847faf67b00e56bb4ba3d525281e2ac306e1f1f353da/sentry_sdk-2.47.0-py2.py3-none-any.whl", hash = "sha256:d72f8c61025b7d1d9e52510d03a6247b280094a327dd900d987717a4fce93412", size = 411088 }, +] + [[package]] name = "setuptools" -version = "75.8.0" +version = "80.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 }, + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486 }, ] [[package]] @@ -3738,11 +4145,11 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.6" +version = "2.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 }, ] [[package]] @@ -3755,23 +4162,23 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.37" +version = "2.0.44" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3b/20/93ea2518df4d7a14ebe9ace9ab8bb92aaf7df0072b9007644de74172b06c/sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb", size = 9626249 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830 } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/21/aaf0cd2e7ee56e464af7cba38a54f9c1203570181ec5d847711f33c9f520/SQLAlchemy-2.0.37-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e", size = 2102915 }, - { url = "https://files.pythonhosted.org/packages/fd/01/6615256759515f13bb7d7b49981326f1f4e80ff1bd92dccd53f99dab79ea/SQLAlchemy-2.0.37-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069", size = 2094095 }, - { url = "https://files.pythonhosted.org/packages/6a/f2/400252bda1bd67da7a35bb2ab84d10a8ad43975d42f15b207a9efb765446/SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1", size = 3076482 }, - { url = "https://files.pythonhosted.org/packages/40/c6/e7e8e894c8f065f96ca202cdb00454d60d4962279b3eb5a81b8766dfa836/SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84", size = 3084750 }, - { url = "https://files.pythonhosted.org/packages/d6/ee/1cdab04b7760e48273f2592037df156afae044e2e6589157673bd2a830c0/SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f", size = 3040575 }, - { url = "https://files.pythonhosted.org/packages/4d/af/2dd456bfd8d4b9750792ceedd828bddf83860f2420545e5effbaf722dae5/SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4", size = 3066113 }, - { url = "https://files.pythonhosted.org/packages/dd/d7/ad997559574f94d7bd895a8a63996afef518d07e9eaf5a2a9cbbcb877c16/SQLAlchemy-2.0.37-cp310-cp310-win32.whl", hash = "sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72", size = 2075239 }, - { url = "https://files.pythonhosted.org/packages/d0/82/141fbed705a21af2d825068831da1d80d720945df60c2b97ddc5133b3714/SQLAlchemy-2.0.37-cp310-cp310-win_amd64.whl", hash = "sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636", size = 2099307 }, - { url = "https://files.pythonhosted.org/packages/3b/36/59cc97c365f2f79ac9f3f51446cae56dfd82c4f2dd98497e6be6de20fb91/SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1", size = 1894113 }, + { url = "https://files.pythonhosted.org/packages/a2/a7/e9ccfa7eecaf34c6f57d8cb0bb7cbdeeff27017cc0f5d0ca90fdde7a7c0d/sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce", size = 2137282 }, + { url = "https://files.pythonhosted.org/packages/b1/e1/50bc121885bdf10833a4f65ecbe9fe229a3215f4d65a58da8a181734cae3/sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985", size = 2127322 }, + { url = "https://files.pythonhosted.org/packages/46/f2/a8573b7230a3ce5ee4b961a2d510d71b43872513647398e595b744344664/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0", size = 3214772 }, + { url = "https://files.pythonhosted.org/packages/4a/d8/c63d8adb6a7edaf8dcb6f75a2b1e9f8577960a1e489606859c4d73e7d32b/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e", size = 3214434 }, + { url = "https://files.pythonhosted.org/packages/ee/a6/243d277a4b54fae74d4797957a7320a5c210c293487f931cbe036debb697/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749", size = 3155365 }, + { url = "https://files.pythonhosted.org/packages/5f/f8/6a39516ddd75429fd4ee5a0d72e4c80639fab329b2467c75f363c2ed9751/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2", size = 3178910 }, + { url = "https://files.pythonhosted.org/packages/43/f0/118355d4ad3c39d9a2f5ee4c7304a9665b3571482777357fa9920cd7a6b4/sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165", size = 2105624 }, + { url = "https://files.pythonhosted.org/packages/61/83/6ae5f9466f8aa5d0dcebfff8c9c33b98b27ce23292df3b990454b3d434fd/sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5", size = 2129240 }, + { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718 }, ] [[package]] @@ -3809,14 +4216,49 @@ wheels = [ [[package]] name = "starlette" -version = "0.45.3" +version = "0.50.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033 }, +] + +[[package]] +name = "streamlit" +version = "1.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { name = "importlib-metadata" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "pympler" }, + { name = "python-dateutil" }, + { name = "requests" }, + { name = "rich" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "tzlocal" }, + { name = "validators" }, + { name = "watchdog", marker = "platform_system != 'Darwin'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } +sdist = { url = "https://files.pythonhosted.org/packages/17/95/89b0f5f51cd006ffb6cc216393793d05029c47e97f140a9bff02f8c291a8/streamlit-1.21.0.tar.gz", hash = "sha256:c72d9639508679c5e411d1f886d213777759501d01975285c049dc30db463c1a", size = 9345809 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, + { url = "https://files.pythonhosted.org/packages/22/fa/5b010610aa136c44a20de6ed9ec0b36867daf7322d0e3c8e626a99fab11a/streamlit-1.21.0-py2.py3-none-any.whl", hash = "sha256:05862598952a9126e16652caa5bc88eeeea7b2773af252e2daccf1c84ceaaef4", size = 9665093 }, ] [[package]] @@ -3876,11 +4318,11 @@ wheels = [ [[package]] name = "tenacity" -version = "9.0.0" +version = "9.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248 }, ] [[package]] @@ -4005,38 +4447,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/48/47b7d25572961a48b1de3729b7a11e835b888e41e0203cca82df95d23b91/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9679b36e3a80921876f31685ab6f7270f3411a4cc51bc2847e80d0e4b5291e27", size = 5085736 }, ] -[[package]] -name = "tensorflow-macos" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "astunparse" }, - { name = "flatbuffers" }, - { name = "gast" }, - { name = "google-pasta" }, - { name = "grpcio" }, - { name = "h5py" }, - { name = "keras" }, - { name = "keras-preprocessing" }, - { name = "libclang" }, - { name = "numpy" }, - { name = "opt-einsum" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "setuptools" }, - { name = "six" }, - { name = "tensorboard" }, - { name = "tensorflow-estimator" }, - { name = "termcolor" }, - { name = "typing-extensions" }, - { name = "wrapt" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/18/d7d06e1c8bf4cafd091d62854b8e6a9e8db176c1b1a5171a586ec17d4b54/tensorflow_macos-2.10.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:dfd1dd478b3ae01e8d578c38083bef68bc838ceaa05a813b6788fe9e6ec19140", size = 211520008 }, - { url = "https://files.pythonhosted.org/packages/6f/a1/3bd220bacb1dcd4eea526c4b5376eddfb0fbb156c9814684dc9be24b7bc8/tensorflow_macos-2.10.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:aa074b5442d3411e5416c5112531d8b78a8c469ca92fa41c0e0cf14428608bf3", size = 245993233 }, -] - [[package]] name = "tensorflow-text" version = "2.10.0" @@ -4044,7 +4454,6 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tensorflow", marker = "platform_machine != 'arm64' or platform_system != 'Darwin'" }, { name = "tensorflow-hub" }, - { name = "tensorflow-macos", marker = "platform_machine == 'arm64' and platform_system == 'Darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ef/64/489c459f46f678ec4822b815ba9dfb1182c351e2208e98b6e83543254b55/tensorflow_text-2.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84b76847189d0d7bc8d4a130f0617e13bc9b810cf1feeb9c49da2906a2f39785", size = 5621257 }, @@ -4054,11 +4463,11 @@ wheels = [ [[package]] name = "termcolor" -version = "2.5.0" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/72/88311445fd44c455c7d553e61f95412cf89054308a1aa2434ab835075fc5/termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f", size = 13057 } +sdist = { url = "https://files.pythonhosted.org/packages/87/56/ab275c2b56a5e2342568838f0d5e3e66a32354adcc159b495e374cda43f5/termcolor-3.2.0.tar.gz", hash = "sha256:610e6456feec42c4bcd28934a8c87a06c3fa28b01561d46aa09a9881b8622c58", size = 14423 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", size = 7755 }, + { url = "https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl", hash = "sha256:a10343879eba4da819353c55cb8049b0933890c2ebf9ad5d3ecd2bb32ea96ea6", size = 7698 }, ] [[package]] @@ -4107,11 +4516,30 @@ wheels = [ [[package]] name = "threadpoolctl" -version = "3.5.0" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, +] + +[[package]] +name = "tiktoken" +version = "0.12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 }, + { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991 }, + { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798 }, + { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865 }, + { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856 }, + { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308 }, + { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697 }, + { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375 }, ] [[package]] @@ -4147,27 +4575,36 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.21.0" +version = "0.21.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/41/c2be10975ca37f6ec40d7abd7e98a5213bb04f284b869c1a24e6504fd94d/tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4", size = 343021 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/2f/402986d0823f8d7ca139d969af2917fefaa9b947d1fb32f6168c509f2492/tokenizers-0.21.4.tar.gz", hash = "sha256:fa23f85fbc9a02ec5c6978da172cdcbac23498c3ca9f3645c5c68740ac007880", size = 351253 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/5c/8b09607b37e996dc47e70d6a7b6f4bdd4e4d5ab22fe49d7374565c7fefaf/tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2", size = 2647461 }, - { url = "https://files.pythonhosted.org/packages/22/7a/88e58bb297c22633ed1c9d16029316e5b5ac5ee44012164c2edede599a5e/tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e", size = 2563639 }, - { url = "https://files.pythonhosted.org/packages/f7/14/83429177c19364df27d22bc096d4c2e431e0ba43e56c525434f1f9b0fd00/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193", size = 2903304 }, - { url = "https://files.pythonhosted.org/packages/7e/db/3433eab42347e0dc5452d8fcc8da03f638c9accffefe5a7c78146666964a/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e", size = 2804378 }, - { url = "https://files.pythonhosted.org/packages/57/8b/7da5e6f89736c2ade02816b4733983fca1c226b0c42980b1ae9dc8fcf5cc/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e", size = 3095488 }, - { url = "https://files.pythonhosted.org/packages/4d/f6/5ed6711093dc2c04a4e03f6461798b12669bc5a17c8be7cce1240e0b5ce8/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba", size = 3121410 }, - { url = "https://files.pythonhosted.org/packages/81/42/07600892d48950c5e80505b81411044a2d969368cdc0d929b1c847bf6697/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273", size = 3388821 }, - { url = "https://files.pythonhosted.org/packages/22/06/69d7ce374747edaf1695a4f61b83570d91cc8bbfc51ccfecf76f56ab4aac/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04", size = 3008868 }, - { url = "https://files.pythonhosted.org/packages/c8/69/54a0aee4d576045b49a0eb8bffdc495634309c823bf886042e6f46b80058/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e", size = 8975831 }, - { url = "https://files.pythonhosted.org/packages/f7/f3/b776061e4f3ebf2905ba1a25d90380aafd10c02d406437a8ba22d1724d76/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b", size = 8920746 }, - { url = "https://files.pythonhosted.org/packages/d8/ee/ce83d5ec8b6844ad4c3ecfe3333d58ecc1adc61f0878b323a15355bcab24/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74", size = 9161814 }, - { url = "https://files.pythonhosted.org/packages/18/07/3e88e65c0ed28fa93aa0c4d264988428eef3df2764c3126dc83e243cb36f/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff", size = 9357138 }, - { url = "https://files.pythonhosted.org/packages/15/b0/dc4572ca61555fc482ebc933f26cb407c6aceb3dc19c301c68184f8cad03/tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a", size = 2202266 }, - { url = "https://files.pythonhosted.org/packages/44/69/d21eb253fa91622da25585d362a874fa4710be600f0ea9446d8d0217cec1/tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c", size = 2389192 }, + { url = "https://files.pythonhosted.org/packages/98/c6/fdb6f72bf6454f52eb4a2510be7fb0f614e541a2554d6210e370d85efff4/tokenizers-0.21.4-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ccc10a7c3bcefe0f242867dc914fc1226ee44321eb618cfe3019b5df3400133", size = 2863987 }, + { url = "https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60", size = 2732457 }, + { url = "https://files.pythonhosted.org/packages/aa/8f/24f39d7b5c726b7b0be95dca04f344df278a3fe3a4deb15a975d194cbb32/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b376f5a1aee67b4d29032ee85511bbd1b99007ec735f7f35c8a2eb104eade5", size = 3012624 }, + { url = "https://files.pythonhosted.org/packages/58/47/26358925717687a58cb74d7a508de96649544fad5778f0cd9827398dc499/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2107ad649e2cda4488d41dfd031469e9da3fcbfd6183e74e4958fa729ffbf9c6", size = 2939681 }, + { url = "https://files.pythonhosted.org/packages/99/6f/cc300fea5db2ab5ddc2c8aea5757a27b89c84469899710c3aeddc1d39801/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c73012da95afafdf235ba80047699df4384fdc481527448a078ffd00e45a7d9", size = 3247445 }, + { url = "https://files.pythonhosted.org/packages/be/bf/98cb4b9c3c4afd8be89cfa6423704337dc20b73eb4180397a6e0d456c334/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f23186c40395fc390d27f519679a58023f368a0aad234af145e0f39ad1212732", size = 3428014 }, + { url = "https://files.pythonhosted.org/packages/75/c7/96c1cc780e6ca7f01a57c13235dd05b7bc1c0f3588512ebe9d1331b5f5ae/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc88bb34e23a54cc42713d6d98af5f1bf79c07653d24fe984d2d695ba2c922a2", size = 3193197 }, + { url = "https://files.pythonhosted.org/packages/f2/90/273b6c7ec78af547694eddeea9e05de771278bd20476525ab930cecaf7d8/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51b7eabb104f46c1c50b486520555715457ae833d5aee9ff6ae853d1130506ff", size = 3115426 }, + { url = "https://files.pythonhosted.org/packages/91/43/c640d5a07e95f1cf9d2c92501f20a25f179ac53a4f71e1489a3dcfcc67ee/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:714b05b2e1af1288bd1bc56ce496c4cebb64a20d158ee802887757791191e6e2", size = 9089127 }, + { url = "https://files.pythonhosted.org/packages/44/a1/dd23edd6271d4dca788e5200a807b49ec3e6987815cd9d0a07ad9c96c7c2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1340ff877ceedfa937544b7d79f5b7becf33a4cfb58f89b3b49927004ef66f78", size = 9055243 }, + { url = "https://files.pythonhosted.org/packages/21/2b/b410d6e9021c4b7ddb57248304dc817c4d4970b73b6ee343674914701197/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3c1f4317576e465ac9ef0d165b247825a2a4078bcd01cba6b54b867bdf9fdd8b", size = 9298237 }, + { url = "https://files.pythonhosted.org/packages/b7/0a/42348c995c67e2e6e5c89ffb9cfd68507cbaeb84ff39c49ee6e0a6dd0fd2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c212aa4e45ec0bb5274b16b6f31dd3f1c41944025c2358faaa5782c754e84c24", size = 9461980 }, + { url = "https://files.pythonhosted.org/packages/3d/d3/dacccd834404cd71b5c334882f3ba40331ad2120e69ded32cf5fda9a7436/tokenizers-0.21.4-cp39-abi3-win32.whl", hash = "sha256:6c42a930bc5f4c47f4ea775c91de47d27910881902b0f20e4990ebe045a415d0", size = 2329871 }, + { url = "https://files.pythonhosted.org/packages/41/f2/fd673d979185f5dcbac4be7d09461cbb99751554ffb6718d0013af8604cb/tokenizers-0.21.4-cp39-abi3-win_amd64.whl", hash = "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", size = 2507568 }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, ] [[package]] @@ -4181,11 +4618,20 @@ wheels = [ [[package]] name = "tomli" -version = "2.2.1" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408 }, +] + +[[package]] +name = "toolz" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, ] [[package]] @@ -4264,20 +4710,21 @@ wheels = [ [[package]] name = "tornado" -version = "6.4.2" +version = "6.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 } +sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821 } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 }, - { url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 }, - { url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 }, - { url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 }, - { url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 }, - { url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 }, - { url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 }, - { url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 }, - { url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 }, - { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, + { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563 }, + { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729 }, + { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295 }, + { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644 }, + { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878 }, + { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549 }, + { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973 }, + { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954 }, + { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023 }, + { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427 }, + { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456 }, ] [[package]] @@ -4303,16 +4750,15 @@ wheels = [ [[package]] name = "transformer-smaller-training-vocab" -version = "0.4.0" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, { name = "torch" }, { name = "transformers", extra = ["sentencepiece", "torch"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/28/5214ff2a93d9cccc55d0679cb8cbc3b9d52f1f07860c92841b16cfeb5026/transformer_smaller_training_vocab-0.4.0.tar.gz", hash = "sha256:d7360ac084786f66f99ef16d621f34acbb0dce6d9a624525d1f7dc8b6c3a49f7", size = 12141 } +sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754 } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/c8/6a02e88256dc48faf3eae5732a94035f4ea0edd91d8224333693111921ba/transformer_smaller_training_vocab-0.4.0-py3-none-any.whl", hash = "sha256:01cb3d8f4818121172e1591a06c3149bf49bc18d6f6f269eb42d2c4ed155cfcc", size = 14120 }, + { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073 }, ] [[package]] @@ -4357,7 +4803,7 @@ wheels = [ [[package]] name = "typer" -version = "0.15.1" +version = "0.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -4365,48 +4811,51 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/dca7b219718afd37a0068f4f2530a727c2b74a8b6e8e0c0080a4c0de4fcd/typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a", size = 99789 } +sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/cc/0a838ba5ca64dc832aa43f727bd586309846b0ffb2ce52422543e6075e8a/typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847", size = 44908 }, + { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028 }, ] [[package]] -name = "types-python-dateutil" -version = "2.9.0.20241206" +name = "typing-extensions" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/60/47d92293d9bc521cd2301e423a358abfac0ad409b3a1606d8fbae1321961/types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb", size = 13802 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/b3/ca41df24db5eb99b00d97f89d7674a90cb6b3134c52fb8121b6d8d30f15c/types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53", size = 14384 }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, ] [[package]] -name = "typing-extensions" -version = "4.12.2" +name = "typing-inspection" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, ] [[package]] name = "tzdata" -version = "2025.1" +version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 }, + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, ] [[package]] name = "tzlocal" -version = "5.2" +version = "5.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "platform_system == 'Windows'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/d3/c19d65ae67636fe63953b20c2e4a8ced4497ea232c43ff8d01db16de8dc0/tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e", size = 30201 } +sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761 } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/3f/c4c51c55ff8487f2e6d0e618dba917e3c3ee2caae6cf0fbb59c9b1876f2e/tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8", size = 17859 }, + { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026 }, ] [[package]] @@ -4429,25 +4878,25 @@ wheels = [ [[package]] name = "urllib3" -version = "2.3.0" +version = "2.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/43/554c2569b62f49350597348fc3ac70f786e3c32e7f19d266e19817812dd3/urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1", size = 432585 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, + { url = "https://files.pythonhosted.org/packages/56/1a/9ffe814d317c5224166b23e7c47f606d6e473712a2fad0f704ea9b99f246/urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f", size = 131083 }, ] [[package]] name = "uvicorn" -version = "0.34.0" +version = "0.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } +sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, + { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109 }, ] [package.optional-dependencies] @@ -4463,16 +4912,25 @@ standard = [ [[package]] name = "uvloop" -version = "0.21.0" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335 }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903 }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499 }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133 }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681 }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261 }, +] + +[[package]] +name = "validators" +version = "0.35.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741 } +sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/76/44a55515e8c9505aa1420aebacf4dd82552e5e15691654894e90d0bd051a/uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f", size = 1442019 }, - { url = "https://files.pythonhosted.org/packages/35/5a/62d5800358a78cc25c8a6c72ef8b10851bdb8cca22e14d9c74167b7f86da/uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d", size = 801898 }, - { url = "https://files.pythonhosted.org/packages/f3/96/63695e0ebd7da6c741ccd4489b5947394435e198a1382349c17b1146bb97/uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26", size = 3827735 }, - { url = "https://files.pythonhosted.org/packages/61/e0/f0f8ec84979068ffae132c58c79af1de9cceeb664076beea86d941af1a30/uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb", size = 3825126 }, - { url = "https://files.pythonhosted.org/packages/bf/fe/5e94a977d058a54a19df95f12f7161ab6e323ad49f4dabc28822eb2df7ea/uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f", size = 3705789 }, - { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c", size = 3800523 }, + { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712 }, ] [[package]] @@ -4486,16 +4944,35 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.29.1" +version = "20.35.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/ca/f23dcb02e161a9bba141b1c08aa50e8da6ea25e6d780528f1d385a3efe25/virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35", size = 7658028 } +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799 } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/9b/599bcfc7064fbe5740919e78c5df18e5dceb0887e676256a1061bb5ae232/virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779", size = 4282379 }, + { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095 }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, ] [[package]] @@ -4518,47 +4995,47 @@ wheels = [ [[package]] name = "watchfiles" -version = "1.0.4" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/02/22fcaed0396730b0d362bc8d1ffb3be2658fd473eecbb2ba84243e157f11/watchfiles-1.0.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ba5bb3073d9db37c64520681dd2650f8bd40902d991e7b4cfaeece3e32561d08", size = 395212 }, - { url = "https://files.pythonhosted.org/packages/e9/3d/ec5a2369a46edf3ebe092c39d9ae48e8cb6dacbde51c4b4f98936c524269/watchfiles-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f25d0ba0fe2b6d2c921cf587b2bf4c451860086534f40c384329fb96e2044d1", size = 384815 }, - { url = "https://files.pythonhosted.org/packages/df/b4/898991cececbe171e67142c31905510203649569d9817848f47c4177ee42/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47eb32ef8c729dbc4f4273baece89398a4d4b5d21a1493efea77a17059f4df8a", size = 450680 }, - { url = "https://files.pythonhosted.org/packages/58/f7/d4aa3000e812cfb5e5c2c6c0a3ec9d0a46a42489a8727edd160631c4e210/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:076f293100db3b0b634514aa0d294b941daa85fc777f9c698adb1009e5aca0b1", size = 455923 }, - { url = "https://files.pythonhosted.org/packages/dd/95/7e2e4c6aba1b02fb5c76d2f6a450b85215921ec5f8f7ad5efd075369563f/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eacd91daeb5158c598fe22d7ce66d60878b6294a86477a4715154990394c9b3", size = 482339 }, - { url = "https://files.pythonhosted.org/packages/bb/67/4265b0fabcc2ef2c9e3e8802ba7908cf718a357ebfb49c72e53787156a48/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13c2ce7b72026cfbca120d652f02c7750f33b4c9395d79c9790b27f014c8a5a2", size = 519908 }, - { url = "https://files.pythonhosted.org/packages/0d/96/b57802d5f8164bdf070befb4fd3dec4edba5a364ec0670965a97eb8098ce/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90192cdc15ab7254caa7765a98132a5a41471cf739513cc9bcf7d2ffcc0ec7b2", size = 501410 }, - { url = "https://files.pythonhosted.org/packages/8b/18/6db0de4e8911ba14e31853201b40c0fa9fea5ecf3feb86b0ad58f006dfc3/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278aaa395f405972e9f523bd786ed59dfb61e4b827856be46a42130605fd0899", size = 452876 }, - { url = "https://files.pythonhosted.org/packages/df/df/092a961815edf723a38ba2638c49491365943919c3526cc9cf82c42786a6/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a462490e75e466edbb9fc4cd679b62187153b3ba804868452ef0577ec958f5ff", size = 615353 }, - { url = "https://files.pythonhosted.org/packages/f3/cf/b85fe645de4ff82f3f436c5e9032379fce37c303f6396a18f9726cc34519/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8d0d0630930f5cd5af929040e0778cf676a46775753e442a3f60511f2409f48f", size = 613187 }, - { url = "https://files.pythonhosted.org/packages/f6/d4/a9fea27aef4dd69689bc3556718c1157a7accb72aa035ece87c1fa8483b5/watchfiles-1.0.4-cp310-cp310-win32.whl", hash = "sha256:cc27a65069bcabac4552f34fd2dce923ce3fcde0721a16e4fb1b466d63ec831f", size = 270799 }, - { url = "https://files.pythonhosted.org/packages/df/02/dbe9d4439f15dd4ad0720b6e039bde9d66d1f830331f34c18eb70fa6608e/watchfiles-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:8b1f135238e75d075359cf506b27bf3f4ca12029c47d3e769d8593a2024ce161", size = 284145 }, - { url = "https://files.pythonhosted.org/packages/6f/06/175d5ac6b838fb319008c0cd981d7bf289317c510154d411d3584ca2b67b/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18", size = 396269 }, - { url = "https://files.pythonhosted.org/packages/86/ee/5db93b0b57dc0587abdbac4149296ee73275f615d790a82cb5598af0557f/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817", size = 386010 }, - { url = "https://files.pythonhosted.org/packages/75/61/fe0dc5fedf152bfc085a53711f740701f6bdb8ab6b5c950402b681d4858b/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0", size = 450913 }, - { url = "https://files.pythonhosted.org/packages/9f/dd/3c7731af3baf1a9957afc643d176f94480921a690ec3237c9f9d11301c08/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d", size = 453474 }, + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318 }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478 }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894 }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065 }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377 }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837 }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456 }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614 }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690 }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459 }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663 }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453 }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611 }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889 }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616 }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413 }, ] [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286 }, ] [[package]] name = "webcolors" -version = "24.11.1" +version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/29/061ec845fb58521848f3739e466efd8250b4b7b98c1b6c5bf4d40b419b7e/webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6", size = 45064 } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", size = 14934 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, ] [[package]] @@ -4572,49 +5049,49 @@ wheels = [ [[package]] name = "websocket-client" -version = "1.8.0" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 }, + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, ] [[package]] name = "websockets" -version = "14.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/54/8359678c726243d19fae38ca14a334e740782336c9f19700858c4eb64a1e/websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5", size = 164394 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/fa/76607eb7dcec27b2d18d63f60a32e60e2b8629780f343bb83a4dbb9f4350/websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885", size = 163089 }, - { url = "https://files.pythonhosted.org/packages/9e/00/ad2246b5030575b79e7af0721810fdaecaf94c4b2625842ef7a756fa06dd/websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397", size = 160741 }, - { url = "https://files.pythonhosted.org/packages/72/f7/60f10924d333a28a1ff3fcdec85acf226281331bdabe9ad74947e1b7fc0a/websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610", size = 160996 }, - { url = "https://files.pythonhosted.org/packages/63/7c/c655789cf78648c01ac6ecbe2d6c18f91b75bdc263ffee4d08ce628d12f0/websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3", size = 169974 }, - { url = "https://files.pythonhosted.org/packages/fb/5b/013ed8b4611857ac92ac631079c08d9715b388bd1d88ec62e245f87a39df/websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980", size = 168985 }, - { url = "https://files.pythonhosted.org/packages/cd/33/aa3e32fd0df213a5a442310754fe3f89dd87a0b8e5b4e11e0991dd3bcc50/websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8", size = 169297 }, - { url = "https://files.pythonhosted.org/packages/93/17/dae0174883d6399f57853ac44abf5f228eaba86d98d160f390ffabc19b6e/websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7", size = 169677 }, - { url = "https://files.pythonhosted.org/packages/42/e2/0375af7ac00169b98647c804651c515054b34977b6c1354f1458e4116c1e/websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f", size = 169089 }, - { url = "https://files.pythonhosted.org/packages/73/8d/80f71d2a351a44b602859af65261d3dde3a0ce4e76cf9383738a949e0cc3/websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d", size = 169026 }, - { url = "https://files.pythonhosted.org/packages/48/97/173b1fa6052223e52bb4054a141433ad74931d94c575e04b654200b98ca4/websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d", size = 163967 }, - { url = "https://files.pythonhosted.org/packages/c0/5b/2fcf60f38252a4562b28b66077e0d2b48f91fef645d5f78874cd1dec807b/websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2", size = 164413 }, - { url = "https://files.pythonhosted.org/packages/10/3d/91d3d2bb1325cd83e8e2c02d0262c7d4426dc8fa0831ef1aa4d6bf2041af/websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29", size = 160773 }, - { url = "https://files.pythonhosted.org/packages/33/7c/cdedadfef7381939577858b1b5718a4ab073adbb584e429dd9d9dc9bfe16/websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c", size = 161007 }, - { url = "https://files.pythonhosted.org/packages/ca/35/7a20a3c450b27c04e50fbbfc3dfb161ed8e827b2a26ae31c4b59b018b8c6/websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2", size = 162264 }, - { url = "https://files.pythonhosted.org/packages/e8/9c/e3f9600564b0c813f2448375cf28b47dc42c514344faed3a05d71fb527f9/websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c", size = 161873 }, - { url = "https://files.pythonhosted.org/packages/3f/37/260f189b16b2b8290d6ae80c9f96d8b34692cf1bb3475df54c38d3deb57d/websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a", size = 161818 }, - { url = "https://files.pythonhosted.org/packages/ff/1e/e47dedac8bf7140e59aa6a679e850c4df9610ae844d71b6015263ddea37b/websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3", size = 164465 }, - { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 }, +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423 }, + { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080 }, + { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312 }, + { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319 }, + { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631 }, + { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016 }, + { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426 }, + { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360 }, + { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388 }, + { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830 }, + { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109 }, + { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343 }, + { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599 }, + { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207 }, + { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155 }, + { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884 }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, ] [[package]] name = "werkzeug" -version = "3.1.3" +version = "3.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925 } +sdist = { url = "https://files.pythonhosted.org/packages/45/ea/b0f8eeb287f8df9066e56e831c7824ac6bab645dd6c7a8f4b2d767944f9b/werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e", size = 864687 } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 }, + { url = "https://files.pythonhosted.org/packages/2f/f9/9e082990c2585c744734f85bec79b5dae5df9c974ffee58fe421652c8e91/werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905", size = 224960 }, ] [[package]] @@ -4628,11 +5105,11 @@ wheels = [ [[package]] name = "widgetsnbextension" -version = "4.0.13" +version = "4.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/56/fc/238c424fd7f4ebb25f8b1da9a934a3ad7c848286732ae04263661eb0fc03/widgetsnbextension-4.0.13.tar.gz", hash = "sha256:ffcb67bc9febd10234a362795f643927f4e0c05d9342c727b65d2384f8feacb6", size = 1164730 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/02/88b65cc394961a60c43c70517066b6b679738caf78506a5da7b88ffcb643/widgetsnbextension-4.0.13-py3-none-any.whl", hash = "sha256:74b2692e8500525cc38c2b877236ba51d34541e6385eeed5aec15a70f88a6c71", size = 2335872 }, + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, ] [[package]] @@ -4646,22 +5123,23 @@ sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581 [[package]] name = "wrapt" -version = "1.17.2" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307 }, - { url = "https://files.pythonhosted.org/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486 }, - { url = "https://files.pythonhosted.org/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777 }, - { url = "https://files.pythonhosted.org/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314 }, - { url = "https://files.pythonhosted.org/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947 }, - { url = "https://files.pythonhosted.org/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778 }, - { url = "https://files.pythonhosted.org/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716 }, - { url = "https://files.pythonhosted.org/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548 }, - { url = "https://files.pythonhosted.org/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334 }, - { url = "https://files.pythonhosted.org/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427 }, - { url = "https://files.pythonhosted.org/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774 }, - { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, + { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481 }, + { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692 }, + { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574 }, + { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688 }, + { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698 }, + { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096 }, + { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878 }, + { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298 }, + { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361 }, + { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035 }, + { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383 }, + { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894 }, + { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046 }, ] [[package]] @@ -4675,11 +5153,11 @@ wheels = [ [[package]] name = "xlsxwriter" -version = "3.2.1" +version = "3.2.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/4f/108b0bada5cfcc47c24ea6181f4c563fbafef50bcc0054089c256b2ae578/XlsxWriter-3.2.1.tar.gz", hash = "sha256:97618759cb264fb6a93397f660cca156ffa9561743b1823dafb60dc4474e1902", size = 202868 } +sdist = { url = "https://files.pythonhosted.org/packages/46/2c/c06ef49dc36e7954e55b802a8b231770d286a9758b3d936bd1e04ce5ba88/xlsxwriter-3.2.9.tar.gz", hash = "sha256:254b1c37a368c444eac6e2f867405cc9e461b0ed97a3233b2ac1e574efb4140c", size = 215940 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/30/040af902cb8a9909d320779d8467aa7590bb91477767fd2b7551f4d91bb5/XlsxWriter-3.2.1-py3-none-any.whl", hash = "sha256:7e8f7c60b7a1660ef791d46ab5de78469cb978b991ca841af61f5832d2f9f4fe", size = 162778 }, + { url = "https://files.pythonhosted.org/packages/3a/0c/3662f4a66880196a590b202f0db82d919dd2f89e99a27fadef91c4a33d41/xlsxwriter-3.2.9-py3-none-any.whl", hash = "sha256:9a5db42bc5dff014806c58a20b9eae7322a134abb6fce3c92c181bfb275ec5b3", size = 175315 }, ] [[package]] @@ -4693,60 +5171,64 @@ wheels = [ [[package]] name = "xxhash" -version = "3.5.0" +version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/8a/0e9feca390d512d293afd844d31670e25608c4a901e10202aa98785eab09/xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212", size = 31970 }, - { url = "https://files.pythonhosted.org/packages/16/e6/be5aa49580cd064a18200ab78e29b88b1127e1a8c7955eb8ecf81f2626eb/xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520", size = 30801 }, - { url = "https://files.pythonhosted.org/packages/20/ee/b8a99ebbc6d1113b3a3f09e747fa318c3cde5b04bd9c197688fadf0eeae8/xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680", size = 220927 }, - { url = "https://files.pythonhosted.org/packages/58/62/15d10582ef159283a5c2b47f6d799fc3303fe3911d5bb0bcc820e1ef7ff4/xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da", size = 200360 }, - { url = "https://files.pythonhosted.org/packages/23/41/61202663ea9b1bd8e53673b8ec9e2619989353dba8cfb68e59a9cbd9ffe3/xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23", size = 428528 }, - { url = "https://files.pythonhosted.org/packages/f2/07/d9a3059f702dec5b3b703737afb6dda32f304f6e9da181a229dafd052c29/xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196", size = 194149 }, - { url = "https://files.pythonhosted.org/packages/eb/58/27caadf78226ecf1d62dbd0c01d152ed381c14c1ee4ad01f0d460fc40eac/xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c", size = 207703 }, - { url = "https://files.pythonhosted.org/packages/b1/08/32d558ce23e1e068453c39aed7b3c1cdc690c177873ec0ca3a90d5808765/xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482", size = 216255 }, - { url = "https://files.pythonhosted.org/packages/3f/d4/2b971e2d2b0a61045f842b622ef11e94096cf1f12cd448b6fd426e80e0e2/xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296", size = 202744 }, - { url = "https://files.pythonhosted.org/packages/19/ae/6a6438864a8c4c39915d7b65effd85392ebe22710412902487e51769146d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415", size = 210115 }, - { url = "https://files.pythonhosted.org/packages/48/7d/b3c27c27d1fc868094d02fe4498ccce8cec9fcc591825c01d6bcb0b4fc49/xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198", size = 414247 }, - { url = "https://files.pythonhosted.org/packages/a1/05/918f9e7d2fbbd334b829997045d341d6239b563c44e683b9a7ef8fe50f5d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442", size = 191419 }, - { url = "https://files.pythonhosted.org/packages/08/29/dfe393805b2f86bfc47c290b275f0b7c189dc2f4e136fd4754f32eb18a8d/xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da", size = 30114 }, - { url = "https://files.pythonhosted.org/packages/7b/d7/aa0b22c4ebb7c3ccb993d4c565132abc641cd11164f8952d89eb6a501909/xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9", size = 30003 }, - { url = "https://files.pythonhosted.org/packages/69/12/f969b81541ee91b55f1ce469d7ab55079593c80d04fd01691b550e535000/xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6", size = 26773 }, - { url = "https://files.pythonhosted.org/packages/ab/9a/233606bada5bd6f50b2b72c45de3d9868ad551e83893d2ac86dc7bb8553a/xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c", size = 29732 }, - { url = "https://files.pythonhosted.org/packages/0c/67/f75276ca39e2c6604e3bee6c84e9db8a56a4973fde9bf35989787cf6e8aa/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986", size = 36214 }, - { url = "https://files.pythonhosted.org/packages/0f/f8/f6c61fd794229cc3848d144f73754a0c107854372d7261419dcbbd286299/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6", size = 32020 }, - { url = "https://files.pythonhosted.org/packages/79/d3/c029c99801526f859e6b38d34ab87c08993bf3dcea34b11275775001638a/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b", size = 40515 }, - { url = "https://files.pythonhosted.org/packages/62/e3/bef7b82c1997579c94de9ac5ea7626d01ae5858aa22bf4fcb38bf220cb3e/xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da", size = 30064 }, +sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845 }, + { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807 }, + { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786 }, + { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830 }, + { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606 }, + { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872 }, + { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217 }, + { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139 }, + { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669 }, + { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018 }, + { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058 }, + { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628 }, + { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577 }, + { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487 }, + { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863 }, ] [[package]] name = "yarl" -version = "1.18.3" +version = "1.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/98/e005bc608765a8a5569f58e650961314873c8469c333616eb40bff19ae97/yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34", size = 141458 }, - { url = "https://files.pythonhosted.org/packages/df/5d/f8106b263b8ae8a866b46d9be869ac01f9b3fb7f2325f3ecb3df8003f796/yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7", size = 94365 }, - { url = "https://files.pythonhosted.org/packages/56/3e/d8637ddb9ba69bf851f765a3ee288676f7cf64fb3be13760c18cbc9d10bd/yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed", size = 92181 }, - { url = "https://files.pythonhosted.org/packages/76/f9/d616a5c2daae281171de10fba41e1c0e2d8207166fc3547252f7d469b4e1/yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde", size = 315349 }, - { url = "https://files.pythonhosted.org/packages/bb/b4/3ea5e7b6f08f698b3769a06054783e434f6d59857181b5c4e145de83f59b/yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b", size = 330494 }, - { url = "https://files.pythonhosted.org/packages/55/f1/e0fc810554877b1b67420568afff51b967baed5b53bcc983ab164eebf9c9/yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5", size = 326927 }, - { url = "https://files.pythonhosted.org/packages/a9/42/b1753949b327b36f210899f2dd0a0947c0c74e42a32de3f8eb5c7d93edca/yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc", size = 319703 }, - { url = "https://files.pythonhosted.org/packages/f0/6d/e87c62dc9635daefb064b56f5c97df55a2e9cc947a2b3afd4fd2f3b841c7/yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd", size = 310246 }, - { url = "https://files.pythonhosted.org/packages/e3/ef/e2e8d1785cdcbd986f7622d7f0098205f3644546da7919c24b95790ec65a/yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990", size = 319730 }, - { url = "https://files.pythonhosted.org/packages/fc/15/8723e22345bc160dfde68c4b3ae8b236e868f9963c74015f1bc8a614101c/yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db", size = 321681 }, - { url = "https://files.pythonhosted.org/packages/86/09/bf764e974f1516efa0ae2801494a5951e959f1610dd41edbfc07e5e0f978/yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62", size = 324812 }, - { url = "https://files.pythonhosted.org/packages/f6/4c/20a0187e3b903c97d857cf0272d687c1b08b03438968ae8ffc50fe78b0d6/yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760", size = 337011 }, - { url = "https://files.pythonhosted.org/packages/c9/71/6244599a6e1cc4c9f73254a627234e0dad3883ece40cc33dce6265977461/yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b", size = 338132 }, - { url = "https://files.pythonhosted.org/packages/af/f5/e0c3efaf74566c4b4a41cb76d27097df424052a064216beccae8d303c90f/yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690", size = 331849 }, - { url = "https://files.pythonhosted.org/packages/8a/b8/3d16209c2014c2f98a8f658850a57b716efb97930aebf1ca0d9325933731/yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6", size = 84309 }, - { url = "https://files.pythonhosted.org/packages/fd/b7/2e9a5b18eb0fe24c3a0e8bae994e812ed9852ab4fd067c0107fadde0d5f0/yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", size = 90484 }, - { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 }, +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517 }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495 }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400 }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545 }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598 }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893 }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240 }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965 }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026 }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637 }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082 }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811 }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223 }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118 }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852 }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012 }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814 }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, ] [[package]] From f85d7cff909c336d2352d862759a29a87461f224 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 5 Dec 2025 19:21:33 +0000 Subject: [PATCH 020/101] =?UTF-8?q?=E2=8F=AA=20Rollback=20to=20previous=20?= =?UTF-8?q?torch=20and=20torchtext=20versions=20to=20avoid=20conflicts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 +- uv.lock | 540 ++----------------------------------------------- 2 files changed, 15 insertions(+), 529 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0086a268..b89395ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,8 +74,8 @@ dependencies = [ "cachetools>=5.5.0", "diskcache>=5.6.3", "scipy<1.14.1", - "torch==2.9.0", - "torchtext==0.18.0", + "torch==1.12.1", + "torchtext==0.13.1", "pytorch-lightning==1.8.3.post1", "tensorflow_text==2.10.0; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'x86_64') or sys_platform == 'win32'", # no ARM64 macOS wheels "psutil==6.1.0", diff --git a/uv.lock b/uv.lock index 401cc267..f22747ea 100644 --- a/uv.lock +++ b/uv.lock @@ -126,23 +126,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, ] -[[package]] -name = "altair" -version = "4.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "entrypoints" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "toolz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/bf/781b607da4c1a2a7211cd570bd7e22e0accd4deaf1074c32ac7344a09339/altair-4.2.2.tar.gz", hash = "sha256:39399a267c49b30d102c10411e67ab26374156a84b1aeb9fcd15140429ba49c5", size = 740430 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/62/47452306e84d4d2e67f9c559380aeb230f5e6ca84fafb428dd36b96a99ba/altair-4.2.2-py3-none-any.whl", hash = "sha256:8b45ebeaf8557f2d760c5c77b79f02ae12aee7c46c27c06014febab6f849bc87", size = 813630 }, -] - [[package]] name = "annotated-types" version = "0.7.0" @@ -301,7 +284,7 @@ wheels = [ [[package]] name = "aymurai" -version = "1.1.13.dev63" +version = "1.1.13.dev29" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -438,15 +421,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, ] -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, -] - [[package]] name = "beautifulsoup4" version = "4.8.2" @@ -634,15 +608,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, ] -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -833,15 +798,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, ] -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, -] - [[package]] name = "dnspython" version = "2.8.0" @@ -900,40 +856,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/52/56eed4828175f48f712a50a994293065afa7cc98cb112992a0b071179b6c/dspy-3.0.4-py3-none-any.whl", hash = "sha256:c0a88c7936f41f6f613ee6ca8cd92e63746ff2bd780e3896615ade7628eb6a6a", size = 285224 }, ] -[[package]] -name = "dspy" -version = "3.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "asyncer" }, - { name = "backoff" }, - { name = "cachetools" }, - { name = "cloudpickle" }, - { name = "diskcache" }, - { name = "gepa" }, - { name = "joblib" }, - { name = "json-repair" }, - { name = "litellm" }, - { name = "magicattr" }, - { name = "numpy" }, - { name = "openai" }, - { name = "optuna" }, - { name = "orjson" }, - { name = "pillow" }, - { name = "pydantic" }, - { name = "regex" }, - { name = "requests" }, - { name = "rich" }, - { name = "tenacity" }, - { name = "tqdm" }, - { name = "xxhash" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/18/0042d299cd5e85fdb381568f0cfcc7769122e8f70ea0a2d33e12fd63e705/dspy-3.0.4.tar.gz", hash = "sha256:cb4529df9a91353a16144d9d94ba6ff25f36fc5adfd921f127f4c49d0e309fb8", size = 236376 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/52/56eed4828175f48f712a50a994293065afa7cc98cb112992a0b071179b6c/dspy-3.0.4-py3-none-any.whl", hash = "sha256:c0a88c7936f41f6f613ee6ca8cd92e63746ff2bd780e3896615ade7628eb6a6a", size = 285224 }, -] - [[package]] name = "ebcdic" version = "1.1.1" @@ -964,15 +886,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294 }, ] -[[package]] -name = "entrypoints" -version = "0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/8d/a7121ffe5f402dc015277d2d31eb82d2187334503a011c18f2e78ecbb9b2/entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", size = 13974 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294 }, -] - [[package]] name = "exceptiongroup" version = "1.3.1" @@ -1149,25 +1062,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122 }, ] -[[package]] -name = "fastuuid" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760 }, - { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748 }, - { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537 }, - { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994 }, - { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003 }, - { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583 }, - { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955 }, - { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763 }, - { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613 }, - { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045 }, - { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122 }, -] - [[package]] name = "filelock" version = "3.20.0" @@ -1833,34 +1727,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110 }, ] -[[package]] -name = "jiter" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652 }, - { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829 }, - { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568 }, - { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052 }, - { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585 }, - { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541 }, - { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423 }, - { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958 }, - { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084 }, - { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054 }, - { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368 }, - { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847 }, - { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144 }, - { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877 }, - { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419 }, - { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212 }, - { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974 }, - { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233 }, - { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537 }, - { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110 }, -] - [[package]] name = "jiwer" version = "3.0.5" @@ -1901,15 +1767,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/53/3a/1b4df9adcd69fee9c9e4b439c13e8c866f2fae520054aede7030b2278be9/json_repair-0.54.2-py3-none-any.whl", hash = "sha256:be51cce5dca97e0c24ebdf61a1ede2449a8a7666012de99467bb7b0afb35179b", size = 29322 }, ] -[[package]] -name = "json-repair" -version = "0.54.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/46/d3a4d9a3dad39bb4a2ad16b8adb9fe2e8611b20b71197fe33daa6768e85d/json_repair-0.54.1.tar.gz", hash = "sha256:d010bc31f1fc66e7c36dc33bff5f8902674498ae5cb8e801ad455a53b455ad1d", size = 38555 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/96/c9aad7ee949cc1bf15df91f347fbc2d3bd10b30b80c7df689ce6fe9332b5/json_repair-0.54.1-py3-none-any.whl", hash = "sha256:016160c5db5d5fe443164927bb58d2dfbba5f43ad85719fa9bc51c713a443ab1", size = 29311 }, -] - [[package]] name = "json5" version = "0.12.1" @@ -2368,14 +2225,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/7e/76b7e0c391bee7e9273725c29c8fe41c4df62a215ce58aa8e3518baee0bb/magicattr-0.1.6-py2.py3-none-any.whl", hash = "sha256:d96b18ee45b5ee83b09c17e15d3459a64de62d538808c2f71182777dd9dbbbdf", size = 4664 }, ] -[[package]] -name = "magicattr" -version = "0.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/7e/76b7e0c391bee7e9273725c29c8fe41c4df62a215ce58aa8e3518baee0bb/magicattr-0.1.6-py2.py3-none-any.whl", hash = "sha256:d96b18ee45b5ee83b09c17e15d3459a64de62d538808c2f71182777dd9dbbbdf", size = 4664 }, -] - [[package]] name = "mako" version = "1.3.10" @@ -2502,19 +2351,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, ] -[[package]] -name = "ml-collections" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, -] - [[package]] name = "more-itertools" version = "10.8.0" @@ -2537,15 +2373,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, ] -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, -] - [[package]] name = "multidict" version = "6.7.0" @@ -2678,15 +2505,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, ] -[[package]] -name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, -] - [[package]] name = "nodeenv" version = "1.9.1" @@ -2740,155 +2558,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, ] -[[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, - { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, - { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, - { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.3.20" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/9d/3dd98852568fb845ec1f7902c90a22b240fe1cbabda411ccedf2fd737b7b/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b0b960da3842212758e4fa4696b94f129090b30e5122fea3c5345916545cff0", size = 124484616 }, - { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, -] - [[package]] name = "oauthlib" version = "3.3.1" @@ -3207,18 +2876,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, ] -[[package]] -name = "proto-plus" -version = "1.26.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, -] - [[package]] name = "protobuf" version = "3.19.6" @@ -3423,19 +3080,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, ] -[[package]] -name = "pydeck" -version = "0.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, -] - [[package]] name = "pygments" version = "2.19.2" @@ -3457,18 +3101,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766 }, ] -[[package]] -name = "pympler" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "platform_system == 'Windows'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/37/c384631908029676d8e7213dd956bb686af303a80db7afbc9be36bc49495/pympler-1.1.tar.gz", hash = "sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424", size = 179954 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766 }, -] - [[package]] name = "pymupdf" version = "1.26.6" @@ -4261,52 +3893,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/fa/5b010610aa136c44a20de6ed9ec0b36867daf7322d0e3c8e626a99fab11a/streamlit-1.21.0-py2.py3-none-any.whl", hash = "sha256:05862598952a9126e16652caa5bc88eeeea7b2773af252e2daccf1c84ceaaef4", size = 9665093 }, ] -[[package]] -name = "streamlit" -version = "1.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "altair" }, - { name = "blinker" }, - { name = "cachetools" }, - { name = "click" }, - { name = "gitpython" }, - { name = "importlib-metadata" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "pillow" }, - { name = "protobuf" }, - { name = "pyarrow" }, - { name = "pydeck" }, - { name = "pympler" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "rich" }, - { name = "toml" }, - { name = "tornado" }, - { name = "typing-extensions" }, - { name = "tzlocal" }, - { name = "validators" }, - { name = "watchdog", marker = "platform_system != 'Darwin'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/17/95/89b0f5f51cd006ffb6cc216393793d05029c47e97f140a9bff02f8c291a8/streamlit-1.21.0.tar.gz", hash = "sha256:c72d9639508679c5e411d1f886d213777759501d01975285c049dc30db463c1a", size = 9345809 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/fa/5b010610aa136c44a20de6ed9ec0b36867daf7322d0e3c8e626a99fab11a/streamlit-1.21.0-py2.py3-none-any.whl", hash = "sha256:05862598952a9126e16652caa5bc88eeeea7b2773af252e2daccf1c84ceaaef4", size = 9665093 }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, -] - [[package]] name = "tabulate" version = "0.9.0" @@ -4542,25 +4128,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375 }, ] -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991 }, - { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798 }, - { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865 }, - { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856 }, - { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308 }, - { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697 }, - { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375 }, -] - [[package]] name = "tinycss2" version = "1.4.0" @@ -4607,15 +4174,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, ] -[[package]] -name = "toml" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, -] - [[package]] name = "tomli" version = "2.3.0" @@ -4634,48 +4192,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, ] -[[package]] -name = "toolz" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, -] - [[package]] name = "torch" -version = "2.9.0" +version = "1.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/86/245c240d2138c17ed572c943c289056c2721abab70810d772c6bf5495b28/torch-2.9.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:030bbfe367379ae6a4ae4042b6c44da25383343b8b3c68abaa9c7231efbaf2dd", size = 104213554 }, - { url = "https://files.pythonhosted.org/packages/58/1d/fd1e88ae0948825efcab7dd66d12bec23f05d4d38ed81573c8d453c14c06/torch-2.9.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:51cb63902182a78e90886e8068befd8ea102af4b00e420263591a3d70c7d3c6c", size = 899795167 }, - { url = "https://files.pythonhosted.org/packages/63/5a/496197b45c14982bef4e079b24c61dc108e3ab0d0cc9718dba9f54f45a46/torch-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:3f6aad4d2f0ee2248bac25339d74858ff846c3969b27d14ac235821f055af83d", size = 109310314 }, - { url = "https://files.pythonhosted.org/packages/58/b0/2b4e647b0fc706e88eb6c253d05511865578f5f67b55fad639bf3272a4a1/torch-2.9.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:413e1654c9203733138858780e184d9fc59442f0b3b209e16f39354eb893db9b", size = 74452019 }, + { url = "https://files.pythonhosted.org/packages/ca/74/7342c7f21449557a8263c925071a55081edd7e9b641404cfe31d6fb71d3b/torch-1.12.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:9c038662db894a23e49e385df13d47b2a777ffd56d9bcd5b832593fab0a7e286", size = 776338835 }, + { url = "https://files.pythonhosted.org/packages/36/b0/4857929aa28dfe26f7de909ebf002b60499edcd7566441a7433df865f9ba/torch-1.12.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4e1b9c14cf13fd2ab8d769529050629a0e68a6fc5cb8e84b4a3cc1dd8c4fe541", size = 55712361 }, + { url = "https://files.pythonhosted.org/packages/b9/25/fc2111599a038aa6c1c618a7dc9246aabc95f899008949ade31213255a0c/torch-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:e9c8f4a311ac29fc7e8e955cfb7733deb5dbe1bdaabf5d4af2765695824b7e0d", size = 162235663 }, + { url = "https://files.pythonhosted.org/packages/37/72/ef80d39a371a9b4a8aadfb22b141972cc67a7075dae69a9d5b8116505ec0/torch-1.12.1-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:976c3f997cea38ee91a0dd3c3a42322785414748d1761ef926b789dfa97c6134", size = 133811424 }, + { url = "https://files.pythonhosted.org/packages/2f/17/8b557dde1cdb5fbe82f90a3f192046c8e508f106456e12f17c87543e6a42/torch-1.12.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:68104e4715a55c4bb29a85c6a8d57d820e0757da363be1ba680fa8cc5be17b52", size = 49099890 }, ] [[package]] @@ -4694,7 +4223,7 @@ wheels = [ [[package]] name = "torchtext" -version = "0.18.0" +version = "0.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -4703,9 +4232,11 @@ dependencies = [ { name = "tqdm" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/94/1e805ef3ec6541de75e8a86c32e00be471d98cdcef5035ad26457bc388cf/torchtext-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5826d5bbfe84a3c533e7e97659f72dbff73e1614c00c06709607d17c8446e09c", size = 2137942 }, - { url = "https://files.pythonhosted.org/packages/d7/4f/9953b4d4b79917e03c393484ea8ce8f46a4cc1745f272cc371550fb7fc05/torchtext-0.18.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:3dc446f74aaa9aebab045fbefd102752675258e72ba447982c65e010e1cfd29a", size = 2021446 }, - { url = "https://files.pythonhosted.org/packages/b2/3d/6f18d551b00bf8babaa3a569d5fd62cba2bd7bbdeaf82167a959352ba56b/torchtext-0.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4bfe9cb7b08cf7ff3473309d9f24ed243c3a847bfbb2c932925551bf7a05892", size = 1949005 }, + { url = "https://files.pythonhosted.org/packages/10/6f/673e3533a296f135fecfccdf2519ecfba7e16971dca5c3bc7a8cc9cfd064/torchtext-0.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aa59df8f9542674311fc23aca147a34256c936c25840c4b6ee3fee47d511ad17", size = 1775853 }, + { url = "https://files.pythonhosted.org/packages/2f/bd/5f27d604d3dc6421b3c0bd24c28f73eecb091fce9dbdcbe7af314f252cee/torchtext-0.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:492a22727181edf5a33fa18587f3430ffbb366c2ea835e0f4f8a08be8d9e859c", size = 1966034 }, + { url = "https://files.pythonhosted.org/packages/48/4e/56352383c30b75becd5faaff8d404eb86f3a2282d72ae52aaad705bf22bf/torchtext-0.13.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f56359165eb00ea2ff998b67727f87e7fa32665c1a46610c9b5a2d5d581095e5", size = 1910382 }, + { url = "https://files.pythonhosted.org/packages/3f/fd/e3eef8b5d691cdeb42506fc9fec2a64ab1549039cdf9e7545a171e4a694e/torchtext-0.13.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:b5bf4f9da5326b6e74318b4a5035abfaf166b3abd822565be685f44947adb8d3", size = 1832850 }, + { url = "https://files.pythonhosted.org/packages/b8/9d/7f9b786637d664579b66ae7c6742d662a91b8e6a9f55a34e22c20883d801/torchtext-0.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5f8c1b426bda2f22e5bfd118c779c12f8612ed1077e5e3a491242bb4ab7ac4d3", size = 2239348 }, ] [[package]] @@ -4792,15 +4323,6 @@ torch = [ { name = "torch" }, ] -[[package]] -name = "triton" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/22/507b6f58a35e05e84381630b2dc2a3cee1a7a2a7eaf4cba857c638a18a24/triton-3.5.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f90de6a6566bb619b4c0adc9855729e1b1b5e26533fca1bf6206e96b6d277a3", size = 159827599 }, - { url = "https://files.pythonhosted.org/packages/0b/eb/09e31d107a5d00eb281aa7e6635ca463e9bca86515944e399480eadb71f8/triton-3.5.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5d3b3d480debf24eaa739623c9a42446b0b77f95593d30eb1f64cd2278cc1f0", size = 170333110 }, -] - [[package]] name = "typer" version = "0.20.0" @@ -4933,15 +4455,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712 }, ] -[[package]] -name = "validators" -version = "0.35.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712 }, -] - [[package]] name = "virtualenv" version = "20.35.4" @@ -4975,24 +4488,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, ] -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, -] - [[package]] name = "watchfiles" version = "1.1.1" @@ -5230,12 +4725,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50e wheels = [ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, ] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, -] From 5ac37a0c120e92cffb0d9f7571d752f968c4635a Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 5 Dec 2025 19:22:11 +0000 Subject: [PATCH 021/101] =?UTF-8?q?=F0=9F=A9=B9=20Fix:=20Add=20missing=20e?= =?UTF-8?q?nvironment=20variable=20for=20OLLAMA=5FHOST=20in=20docker-compo?= =?UTF-8?q?se?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index b9f2ab12..9a0b3da7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,8 @@ services: env_file: - .env - .env.common + environment: + OLLAMA_HOST: http://ollama:11434 volumes: - ./resources/cache:/resources/cache @@ -26,6 +28,8 @@ services: dockerfile: ./docker/api/Dockerfile target: aymurai-api-full restart: always + environment: + OLLAMA_HOST: http://ollama:11434 deploy: resources: limits: From c1a8a9a279cab2d445e51a42b3df7a855632f1f7 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 5 Dec 2025 19:26:31 +0000 Subject: [PATCH 022/101] =?UTF-8?q?=F0=9F=93=9D=20Add=20anonymization=20pi?= =?UTF-8?q?peline=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/anonymization/README.md | 33 + docs/anonymization/pipeline.excalidraw | 3892 ++++++++++++++++++++++++ 2 files changed, 3925 insertions(+) create mode 100644 docs/anonymization/README.md create mode 100644 docs/anonymization/pipeline.excalidraw diff --git a/docs/anonymization/README.md b/docs/anonymization/README.md new file mode 100644 index 00000000..899a2081 --- /dev/null +++ b/docs/anonymization/README.md @@ -0,0 +1,33 @@ +## Pipeline actual de anonimización + +1. `/misc/document-extract` recibe el DOCX/PDF y devuelve el payload estructurado con `document_id` y la lista de párrafos. +2. `/anonymizer/predict` procesa cada párrafo y retorna las entidades detectadas (`aymurai_label`, texto y offsets) que el frontend reúne sobre el texto completo. Las predicciones originales se persisten en la base de datos. +3. Se validan manualmente las etiquetas consolidadas directamente en la interfaz gráfica. Al confirmar, se persisten las validaciones en la base de datos. +4. `/anonymizer/anonymize-document` aplica los reemplazos según las entidades confirmadas y genera el documento anonimizado listo para descarga. + +## Anonimización con Desambiguación de Entidades Canónicas + +1. `/misc/document-extract` recibe el DOCX/PDF y devuelve el payload estructurado (`document_id`, lista de párrafos) tal como hoy. +2. Se procesa cada párrafo con `/anonymizer/predict`, obteniendo los resultados de NER (`aymurai_label`, texto, offsets, contexto previo y posterior y otros atributos). +3. Se consolida la inferencia: agrupamos todas las menciones detectadas y armamos un paquete enriquecido con contexto circundante (párrafo u oraciones vecinas). +4. Módulo de fuzzy-matching cruza cada mención extraida para sugerir candidatos de `CanonicalEntities` (`canonical_text` y `aliases`). +5. El contexto del paso 3 más las sugerencias del paso 4 son insumo para el servicio de desambiguación (LLM); el modelo se encarga de validar los `CanonicalEntities` sugeridos o proponer nuevos, al tiempo que infiere atributos adicionales (rol procesal), devolviendo una lista refinada de `CanonicalEntities`. +6. Con esa lista se ejecuta el mapeo de `Entity` a `CanonicalEntity`: cada mención NER queda anotada con un `canonical_entity_id` y un `aymurai_label_suffix` y cualquier atributo inferido, generando la desambiguación de entidades. +7. El frontend consume esos outputs y muestra el texto original con dos niveles de revisión: etiquetas NER y asignación canónica. +8. Tras la validación manual, el cliente invoca `/anonymizer/anonymize-document`, que ahora utiliza el `aymurai_label_suffix` para aplicar reemplazos consistentes y generar el documento anonimizado final. +9. El documento anonimizado queda disponible para descarga y se registran en la base de datos las decisiones finales (etiquetas y canónicos). + +## Plan de evaluación incremental + +El objetivo general es optimizar la extracción y desambiguación de entidades. Para avanzar con criterio orientado a métricas mediremos cada capa y el sistema end-to-end. + +- **NER**: aunque el modelo actual cubre el caso de uso, debemos planificar un nuevo ciclo de entrenamiento y finetuning. Será importante consolidar un dataset público anotado con las entidades a detectar, para lo cual podemos aprovechar repositorios públicos de documentos legales de Argentina (e idealmente, del resto de latinoamérica). Para el etiquetado automático, podemos combinar predicciones del NER actual con las extracciones obtenidas usando [`langextract`](/notebooks/experiments/anonymization/05-langextract.ipynb), y conservar solo las coincidencias exactas entre ambos sistemas para minimizar ruido. Luego, se deberán realizar algunas revisiones manuales para asegurar calidad. Este dataset nos permitirá definir un split de evaluación estable y automatizar el cálculo de F1 por etiqueta para comparar versiones de forma consistente. +- **Heurística de agrupación**: vamos a contrastar variantes como Levenshtein normalizada, token fuzzy matching o TF-IDF con coseno. Cada método se ejecutará sobre predicciones reales del NER (para medir robustez ante ruido) y sobre etiquetas existentes (para estimar el techo teórico). La métrica utilizada para evaluar será aquella definida en `/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md`, contra un conjunto de `CanonicalEntities` ya anotados. +- **LLM para desambiguación**: la calidad depende de cómo se promptea y del contexto entregado. Deberíamos experimentar con distintas ventanas de contexto variable (documento completo, párrafos que contienen entidades, oraciones vecinas, metadata, etc.), variantes de prompt (instrucciones directas versus cadenas guiadas) y configuraciones de temperatura/top-p. Cada variante se evaluará con la métrica anteriormente mencionada. + +Finalmente consolidaremos una evaluación integrada combinando el mejor NER disponible, la heurística con mayor cobertura y baja tasa de falsos positivos y la versión de prompt más precisa. El benchmark se ejecutará sobre un conjunto de documentos de validación con etiquetas canónicas revisadas. + +## Consideraciones de integración con frontend + +- Necesitamos habilitar una lista configurable de entidades a excluir de la anonimización. El frontend deberá permitir que la persona usuaria decida, por ejemplo, mantener visibles o no entidades específicas o menciones a funcionariaos públicos según su rol procesal. +- Las exclusiones y la edición manual posterior impactan directamente en el ordenamiento de los `aymurai_label_suffix`. Habrá que recalcular los sufijos luego de aplicar exclusiones y validaciones para evitar huecos o inconsistencias entre el texto mostrado y los reemplazos finales. diff --git a/docs/anonymization/pipeline.excalidraw b/docs/anonymization/pipeline.excalidraw new file mode 100644 index 00000000..358c538e --- /dev/null +++ b/docs/anonymization/pipeline.excalidraw @@ -0,0 +1,3892 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "mprOUEQImobIiF7TwmSu5", + "type": "rectangle", + "x": 323.33333333333337, + "y": 212.14179104477614, + "width": 215.66666666666663, + "height": 280.0447761194029, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a0", + "roundness": { + "type": 3 + }, + "seed": 1252894395, + "version": 114, + "versionNonce": 929231579, + "isDeleted": false, + "boundElements": [ + { + "id": "WOvaiSIhXDUuFwvEHM9bo", + "type": "arrow" + } + ], + "updated": 1764873895187, + "link": null, + "locked": false + }, + { + "id": "j4e8O-DqInLIe6FPmm6uf", + "type": "line", + "x": 366.7885572139304, + "y": 252.37810945273628, + "width": 94.95771144278605, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a1", + "roundness": { + "type": 2 + }, + "seed": 1993017845, + "version": 82, + "versionNonce": 612513819, + "isDeleted": false, + "boundElements": null, + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 94.95771144278605, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "VvUrRcwOayuSCWD_BEIZ-", + "type": "line", + "x": 362.4084692677094, + "y": 279.52565900841427, + "width": 48.28358208955214, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a2", + "roundness": { + "type": 2 + }, + "seed": 1917498107, + "version": 185, + "versionNonce": 1834646715, + "isDeleted": false, + "boundElements": [], + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.28358208955214, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "XF0GuSC8OHCflsYFqZIj-", + "type": "line", + "x": 362.95503103842134, + "y": 309.29823770215256, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a3", + "roundness": { + "type": 2 + }, + "seed": 954781173, + "version": 215, + "versionNonce": 1208036699, + "isDeleted": false, + "boundElements": [], + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "ePQ9UG1U9o78Pyz1DIpJG", + "type": "text", + "x": 376.4452736318408, + "y": 538.8606965174129, + "width": 82.85454631444826, + "height": 80.47263681592038, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a4", + "roundness": null, + "seed": 899224181, + "version": 96, + "versionNonce": 1204341243, + "isDeleted": false, + "boundElements": null, + "updated": 1764873895188, + "link": null, + "locked": false, + "text": ".docx\n.pdf", + "fontSize": 32.189054726368155, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": ".docx\n.pdf", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "50Qya96m5ryhBOeNuo5sd", + "type": "line", + "x": 361.96019900497515, + "y": 337.67910447761193, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a5", + "roundness": { + "type": 2 + }, + "seed": 1599168565, + "version": 113, + "versionNonce": 1907610267, + "isDeleted": false, + "boundElements": null, + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "euPm7o0scLf0nKXvH2Mi-", + "type": "line", + "x": 369.12473982912934, + "y": 364.5779783514023, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a6", + "roundness": { + "type": 2 + }, + "seed": 1599660891, + "version": 147, + "versionNonce": 108316475, + "isDeleted": false, + "boundElements": [], + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "RTz0Ti8oU16rW7TBEfaWk", + "type": "line", + "x": 369.4772828409415, + "y": 390.08391000778613, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a7", + "roundness": { + "type": 2 + }, + "seed": 1240160981, + "version": 253, + "versionNonce": 208545755, + "isDeleted": false, + "boundElements": [], + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "ovlQj2YD-86eShmKhZsbR", + "type": "line", + "x": 368.4824508074953, + "y": 418.4647767832455, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a8", + "roundness": { + "type": 2 + }, + "seed": 1975728181, + "version": 151, + "versionNonce": 431125627, + "isDeleted": false, + "boundElements": [], + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "EtWsq0p_WPVrVIpbECqaR", + "type": "line", + "x": 375.6469916316496, + "y": 445.36365065703586, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "6mIBkNFpjb5VS1uhyvbNS" + ], + "frameId": null, + "index": "a9", + "roundness": { + "type": 2 + }, + "seed": 158886293, + "version": 185, + "versionNonce": 1577331995, + "isDeleted": false, + "boundElements": [], + "updated": 1764873895188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "WOvaiSIhXDUuFwvEHM9bo", + "type": "arrow", + "x": 544.801292623903, + "y": 335.6285710087246, + "width": 276.37331055069967, + "height": 0.9929830550302654, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aA", + "roundness": { + "type": 2 + }, + "seed": 910188603, + "version": 458, + "versionNonce": 376827547, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "2tskJJoi5g4IGXcOp3rfQ" + } + ], + "updated": 1764875808259, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 276.37331055069967, + -0.9929830550302654 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "mprOUEQImobIiF7TwmSu5", + "focus": -0.11485898447026521, + "gap": 5.801292623903009 + }, + "endBinding": { + "elementId": "R8DpmX_gFC_B_zQb3gs_d", + "focus": 0.058585250919652596, + "gap": 26.273809523809632 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "2tskJJoi5g4IGXcOp3rfQ", + "type": "text", + "x": 600.4880623401708, + "y": 310.1320794812094, + "width": 164.99977111816406, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aB", + "roundness": null, + "seed": 773447163, + "version": 42, + "versionNonce": 491638229, + "isDeleted": false, + "boundElements": null, + "updated": 1764875807159, + "link": null, + "locked": false, + "text": "/misc/document-\nextract", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "WOvaiSIhXDUuFwvEHM9bo", + "originalText": "/misc/document-extract", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "R8DpmX_gFC_B_zQb3gs_d", + "type": "text", + "x": 847.4484126984123, + "y": 224.08730158730157, + "width": 536.5656565656566, + "height": 232.7272727272728, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aE", + "roundness": null, + "seed": 890582459, + "version": 272, + "versionNonce": 1768355637, + "isDeleted": false, + "boundElements": [ + { + "id": "WOvaiSIhXDUuFwvEHM9bo", + "type": "arrow" + } + ], + "updated": 1764874730850, + "link": null, + "locked": false, + "text": "{\n \"document_id\": ,\n \"document\": [\n \"R., MATIAS EZEQUIEL SOBRE\n128 1 párr\",\n ...\n ]\n}", + "fontSize": 23.27272727272728, + "fontFamily": 8, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{\n \"document_id\": ,\n \"document\": [\n \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n ...\n ]\n}", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "9wZT6gadfEtFJsnK6a38y", + "type": "arrow", + "x": 1160.80487286459, + "y": 452.14285714285705, + "width": 348.62970232164366, + "height": 56.216370515816834, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aF", + "roundness": { + "type": 2 + }, + "seed": 1750164379, + "version": 998, + "versionNonce": 411091643, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "1kzq54A47HhfpjGS19GQs" + } + ], + "updated": 1764876692331, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 151.88163507191734, + 49.64285714285717 + ], + [ + 348.62970232164366, + 56.216370515816834 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": { + "elementId": "XNFeqRFFVQOQ2Amv9KiKf", + "focus": -0.20762051337520257, + "gap": 22.16146671210572 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "fixedSegments": [ + { + "index": 2, + "start": [ + 0, + 123.33333333333314 + ], + "end": [ + 111.76666666666688, + 123.33333333333314 + ] + }, + { + "index": 3, + "start": [ + 111.76666666666688, + 123.33333333333314 + ], + "end": [ + 111.76666666666688, + 110.84286406975184 + ] + } + ], + "startIsSpecial": false, + "endIsSpecial": false + }, + { + "id": "1kzq54A47HhfpjGS19GQs", + "type": "text", + "x": 1208.1866528950034, + "y": 489.2857142857142, + "width": 208.9997100830078, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aFV", + "roundness": null, + "seed": 1033291995, + "version": 30, + "versionNonce": 40809307, + "isDeleted": false, + "boundElements": null, + "updated": 1764876692331, + "link": null, + "locked": false, + "text": "/anonymizer/predict", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "9wZT6gadfEtFJsnK6a38y", + "originalText": "/anonymizer/predict", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "XNFeqRFFVQOQ2Amv9KiKf", + "type": "rectangle", + "x": 1526.4365079365075, + "y": 256.86507936507934, + "width": 521.6666666666664, + "height": 428.88888888888886, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aH", + "roundness": { + "type": 3 + }, + "seed": 477388725, + "version": 215, + "versionNonce": 1294039099, + "isDeleted": false, + "boundElements": [ + { + "id": "dHelCwKVUG5VTmm5vyDz_", + "type": "arrow" + }, + { + "id": "X1lDBhgBqAfbBo2Wh3tA1", + "type": "arrow" + }, + { + "id": "9wZT6gadfEtFJsnK6a38y", + "type": "arrow" + } + ], + "updated": 1764874713403, + "link": null, + "locked": false + }, + { + "id": "DDpm2VgzVTuFYgzzSeDoT", + "type": "text", + "x": 1698.3106526268848, + "y": 697.4206349206348, + "width": 127.13986206054688, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aI", + "roundness": null, + "seed": 781671541, + "version": 183, + "versionNonce": 198505051, + "isDeleted": false, + "boundElements": [ + { + "id": "qJLbJGOfzrt9cwONItkng", + "type": "arrow" + } + ], + "updated": 1764875154944, + "link": null, + "locked": false, + "text": "NER outputs", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "NER outputs", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "qJLbJGOfzrt9cwONItkng", + "type": "arrow", + "x": 1759.2849840986328, + "y": 751.4206349206347, + "width": 5.191199117050928, + "height": 178.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aJ", + "roundness": { + "type": 2 + }, + "seed": 1942697813, + "version": 481, + "versionNonce": 1156232859, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "i_EvQstHW-Ge2wvbLPeQ_" + } + ], + "updated": 1764875159518, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 5.191199117050928, + 178.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "DDpm2VgzVTuFYgzzSeDoT", + "focus": 0.05947616440110493, + "gap": 14 + }, + "endBinding": { + "elementId": "QMg1bW0iJLThqV1wPLnRu", + "focus": -0.39612694313383184, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "i_EvQstHW-Ge2wvbLPeQ_", + "type": "text", + "x": 1654.8938758278896, + "y": 786.9206349206347, + "width": 204.59686279296875, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aJG", + "roundness": null, + "seed": 1379805179, + "version": 123, + "versionNonce": 1293571995, + "isDeleted": false, + "boundElements": null, + "updated": 1764875154945, + "link": null, + "locked": false, + "text": "fuzzy matching\napproach", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qJLbJGOfzrt9cwONItkng", + "originalText": "fuzzy matching approach", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "QMg1bW0iJLThqV1wPLnRu", + "type": "text", + "x": 1498.6190476190475, + "y": 929.9206349206347, + "width": 905.5525030525026, + "height": 372.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aL", + "roundness": null, + "seed": 101534011, + "version": 624, + "versionNonce": 869890651, + "isDeleted": false, + "boundElements": [ + { + "id": "qJLbJGOfzrt9cwONItkng", + "type": "arrow" + }, + { + "id": "ZMKT-Hy2-Yjk8g0ssbD0q", + "type": "arrow" + } + ], + "updated": 1764876718291, + "link": null, + "locked": false, + "text": "[\n {\n \"aymurai_label\": \"PER\",\n \"canonical_text\": \"MATIAS EZEQUIEL R.\",\n \"aliases\": [\n \"MATIAS EZEQUIEL R.\",\n \"R., MATÍAS EZEQUIEL\",\n \"Matías Ezequiel R.\"\n ],\n \"attributes\": {}\n \n }\n]", + "fontSize": 22.923076923076923, + "fontFamily": 8, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[\n {\n \"aymurai_label\": \"PER\",\n \"canonical_text\": \"MATIAS EZEQUIEL R.\",\n \"aliases\": [\n \"MATIAS EZEQUIEL R.\",\n \"R., MATÍAS EZEQUIEL\",\n \"Matías Ezequiel R.\"\n ],\n \"attributes\": {}\n \n }\n]", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "ZMKT-Hy2-Yjk8g0ssbD0q", + "type": "arrow", + "x": 1779.442822764371, + "y": 1253.9206349206347, + "width": 1.335206841328045, + "height": 212.22319297821105, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aM", + "roundness": { + "type": 2 + }, + "seed": 1658264629, + "version": 582, + "versionNonce": 1501063611, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "cPRb_-k-P_xgsphynZa-B" + } + ], + "updated": 1764876718291, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -1.335206841328045, + 212.22319297821105 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "QMg1bW0iJLThqV1wPLnRu", + "focus": 0.37688413952517746, + "gap": 14 + }, + "endBinding": { + "elementId": "HT4dCz_WP0No85IzXVlCJ", + "focus": 0.01747376853034287, + "gap": 14 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "cPRb_-k-P_xgsphynZa-B", + "type": "text", + "x": 1680.8807133568653, + "y": 1277.5159805629846, + "width": 186.99974060058594, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aMV", + "roundness": null, + "seed": 1547919451, + "version": 55, + "versionNonce": 979131387, + "isDeleted": false, + "boundElements": null, + "updated": 1764875143575, + "link": null, + "locked": false, + "text": "CanonicalEntities\n", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ZMKT-Hy2-Yjk8g0ssbD0q", + "originalText": "CanonicalEntities\n", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "HT4dCz_WP0No85IzXVlCJ", + "type": "diamond", + "x": 1572.2377265143011, + "y": 1491.9307081807083, + "width": 404.2857142857141, + "height": 299.9999999999999, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aN", + "roundness": { + "type": 2 + }, + "seed": 474391803, + "version": 219, + "versionNonce": 917939349, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "6BbvmS0QuJA92jEgUAM7-" + }, + { + "id": "ZMKT-Hy2-Yjk8g0ssbD0q", + "type": "arrow" + }, + { + "id": "dHelCwKVUG5VTmm5vyDz_", + "type": "arrow" + } + ], + "updated": 1764876727910, + "link": null, + "locked": false + }, + { + "id": "6BbvmS0QuJA92jEgUAM7-", + "type": "text", + "x": 1748.381054499792, + "y": 1624.4307081807083, + "width": 51.856201171875, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aO", + "roundness": null, + "seed": 827091643, + "version": 146, + "versionNonce": 459352021, + "isDeleted": false, + "boundElements": null, + "updated": 1764875170814, + "link": null, + "locked": false, + "text": "LLM", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "HT4dCz_WP0No85IzXVlCJ", + "originalText": "LLM", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "dHelCwKVUG5VTmm5vyDz_", + "type": "arrow", + "x": 2063.285201930389, + "y": 694.3135258107342, + "width": 474.8611910477862, + "height": 844.0773032721003, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aR", + "roundness": { + "type": 2 + }, + "seed": 2016671099, + "version": 642, + "versionNonce": 611520693, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "GTYZaUFv22_vAQroP4Ots" + } + ], + "updated": 1764876728139, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 290.8894012442138, + 434.06705471007433 + ], + [ + -183.9717898035724, + 844.0773032721003 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "XNFeqRFFVQOQ2Amv9KiKf", + "focus": -0.3128704896896401, + "gap": 28.317023962699135 + }, + "endBinding": { + "elementId": "HT4dCz_WP0No85IzXVlCJ", + "focus": -0.07412509117026206, + "gap": 24.87677840035615 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "GTYZaUFv22_vAQroP4Ots", + "type": "text", + "x": 2323.4000670417904, + "y": 1024.213913854142, + "width": 261.549072265625, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aRV", + "roundness": null, + "seed": 1799358171, + "version": 29, + "versionNonce": 916759547, + "isDeleted": false, + "boundElements": null, + "updated": 1764873079510, + "link": null, + "locked": false, + "text": "Entities in context", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dHelCwKVUG5VTmm5vyDz_", + "originalText": "Entities in context", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "fQd08AUXM4h1r1rt1GEYO", + "type": "arrow", + "x": 1531.7321526774883, + "y": 1643.1003241404883, + "width": 262.51194713959944, + "height": 2.3392319195597793, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aT", + "roundness": { + "type": 2 + }, + "seed": 860487771, + "version": 330, + "versionNonce": 363111093, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "NX_RkmyZ4ShoFSsJ4jTLw" + } + ], + "updated": 1764875170815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -262.51194713959944, + -2.3392319195597793 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "NX_RkmyZ4ShoFSsJ4jTLw", + "type": "text", + "x": 576.9763088073955, + "y": 1091.9307081807083, + "width": 186.99974060058594, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aU", + "roundness": null, + "seed": 410991867, + "version": 62, + "versionNonce": 810030421, + "isDeleted": false, + "boundElements": [], + "updated": 1764875170815, + "link": null, + "locked": false, + "text": "CanonicalEntities\n", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fQd08AUXM4h1r1rt1GEYO", + "originalText": "CanonicalEntities\n", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "IEM1W5vPfKwT3lgRgCi-R", + "type": "text", + "x": 694.7031440781441, + "y": 1395.8730158730161, + "width": 905.5525030525026, + "height": 487.1153846153846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aW", + "roundness": null, + "seed": 202961365, + "version": 797, + "versionNonce": 518433403, + "isDeleted": false, + "boundElements": [ + { + "id": "ZMKT-Hy2-Yjk8g0ssbD0q", + "type": "arrow" + } + ], + "updated": 1764875186373, + "link": null, + "locked": false, + "text": "[\n {\n \"entity_id\": ,\n \"aymurai_label\": \"PER\",\n \"canonical_text\": \"MATIAS EZEQUIEL R.\",\n \"aliases\": [\n \"MATIAS EZEQUIEL R.\",\n \"R., MATÍAS EZEQUIEL\",\n \"Matías Ezequiel R.\"\n ],\n \"attributes\": {\n \"role\": \"Acusado/a \n }\n \n },\n ...\n]", + "fontSize": 22.923076923076923, + "fontFamily": 8, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[\n {\n \"entity_id\": ,\n \"aymurai_label\": \"PER\",\n \"canonical_text\": \"MATIAS EZEQUIEL R.\",\n \"aliases\": [\n \"MATIAS EZEQUIEL R.\",\n \"R., MATÍAS EZEQUIEL\",\n \"Matías Ezequiel R.\"\n ],\n \"attributes\": {\n \"role\": \"Acusado/a \n }\n \n },\n ...\n]", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "X1lDBhgBqAfbBo2Wh3tA1", + "type": "arrow", + "x": 1522.927714874569, + "y": 690.6888562602353, + "width": 1016.253111699966, + "height": 1419.4587535518317, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aY", + "roundness": { + "type": 2 + }, + "seed": 1525774261, + "version": 379, + "versionNonce": 916502267, + "isDeleted": false, + "boundElements": null, + "updated": 1764874776673, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -1016.253111699966, + 794.7674929461141 + ], + [ + -678.7729798846242, + 1419.4587535518317 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "XNFeqRFFVQOQ2Amv9KiKf", + "focus": -0.03022916805647842, + "gap": 17.297044401543584 + }, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "bLpxUpm3l_5AdE-wefxGf", + "type": "arrow", + "x": 1033.2850735335269, + "y": 1775.4563492063494, + "width": 0.08162462097448042, + "height": 270.3395345008357, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aZ", + "roundness": { + "type": 2 + }, + "seed": 1621637429, + "version": 264, + "versionNonce": 980985435, + "isDeleted": false, + "boundElements": null, + "updated": 1764876700324, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.08162462097448042, + 270.3395345008357 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": { + "elementId": "-PgdE-XG4rOV6rnyozIFX", + "focus": -0.010707089043139571, + "gap": 30.459273173000824 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "-PgdE-XG4rOV6rnyozIFX", + "type": "diamond", + "x": 762.8365384615385, + "y": 2065.4563492063494, + "width": 544.2857142857139, + "height": 342.49999999999994, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aa", + "roundness": { + "type": 2 + }, + "seed": 987028059, + "version": 397, + "versionNonce": 1724613083, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Xwf273IrzwAMLQrLCkCOu" + }, + { + "id": "3VriZd7FuEOwMpdPbnIiX", + "type": "arrow" + }, + { + "id": "bLpxUpm3l_5AdE-wefxGf", + "type": "arrow" + } + ], + "updated": 1764876697912, + "link": null, + "locked": false + }, + { + "id": "Xwf273IrzwAMLQrLCkCOu", + "type": "text", + "x": 919.4074406047444, + "y": 2184.0813492063494, + "width": 231.0010528564453, + "height": 105, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ab", + "roundness": null, + "seed": 1570690811, + "version": 369, + "versionNonce": 1402964917, + "isDeleted": false, + "boundElements": [], + "updated": 1764875219055, + "link": null, + "locked": false, + "text": "Entity to\nCanonicalEntity\nmapping", + "fontSize": 28, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "-PgdE-XG4rOV6rnyozIFX", + "originalText": "Entity to CanonicalEntity mapping", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "YWHB-Nw2VxgFSkL-pcYdK", + "type": "text", + "x": 1035.5431163075204, + "y": 3213.0399522268453, + "width": 223.87255859375, + "height": 80.21844660194176, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "al", + "roundness": null, + "seed": 1428684437, + "version": 332, + "versionNonce": 1630622267, + "isDeleted": false, + "boundElements": [ + { + "id": "j-ZCuk6tMAZF7uiWTtIk4", + "type": "arrow" + } + ], + "updated": 1764875186373, + "link": null, + "locked": false, + "text": "Disambiguated\nNER outputs", + "fontSize": 32.087378640776706, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Disambiguated\nNER outputs", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "3VriZd7FuEOwMpdPbnIiX", + "type": "arrow", + "x": 1049.3582234833063, + "y": 2398.7390090918116, + "width": 56.8350216132967, + "height": 206.2823298539638, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "am", + "roundness": { + "type": 2 + }, + "seed": 479819349, + "version": 456, + "versionNonce": 528370965, + "isDeleted": false, + "boundElements": [], + "updated": 1764875219055, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 56.8350216132967, + 206.2823298539638 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "-PgdE-XG4rOV6rnyozIFX", + "focus": 0.11433848854056756, + "gap": 3.6805732632926023 + }, + "endBinding": { + "elementId": "RCTW7-Twmc0vzPbjJZlva", + "focus": 0.012854192325188162, + "gap": 17.9562212017031 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "j-ZCuk6tMAZF7uiWTtIk4", + "type": "arrow", + "x": 1149.7230307134546, + "y": 3304.34173216212, + "width": 0.0530047510710574, + "height": 179.03128371089633, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "an", + "roundness": { + "type": 2 + }, + "seed": 1544894267, + "version": 234, + "versionNonce": 63933787, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0530047510710574, + 179.03128371089633 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "YWHB-Nw2VxgFSkL-pcYdK", + "focus": -0.12996340741750428, + "gap": 11.08333333333303 + }, + "endBinding": { + "elementId": "htYeX7jml0k3AqG6W3Q_x", + "focus": -0.1090092112744266, + "gap": 22.666666666666515 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "htYeX7jml0k3AqG6W3Q_x", + "type": "image", + "x": 635.4793956043954, + "y": 3506.039682539683, + "width": 1024, + "height": 473, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ao", + "roundness": null, + "seed": 1128147605, + "version": 156, + "versionNonce": 748760533, + "isDeleted": false, + "boundElements": [ + { + "id": "j-ZCuk6tMAZF7uiWTtIk4", + "type": "arrow" + } + ], + "updated": 1764879496527, + "link": null, + "locked": false, + "status": "pending", + "fileId": "7828d57068931cdd86458a8cbc891ed6ee122d38", + "scale": [ + 1, + 1 + ], + "crop": null + }, + { + "id": "a9YgxexRbVJcxPwYgreZn", + "type": "arrow", + "x": 1147.7941516061107, + "y": 4019.4978280481223, + "width": 2.5974080402759228, + "height": 247.43565838777295, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b01", + "roundness": { + "type": 2 + }, + "seed": 1067879003, + "version": 921, + "versionNonce": 230655093, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "elej2TV9eJ1qTHpBpx-2H" + } + ], + "updated": 1764879496527, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.3147560017152955, + 139.37773758213234 + ], + [ + 2.2826520385606273, + 247.43565838777295 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "elej2TV9eJ1qTHpBpx-2H", + "type": "text", + "x": 1036.3792216542001, + "y": 4122.625565630255, + "width": 167.20034790039062, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b01G", + "roundness": null, + "seed": 553249339, + "version": 35, + "versionNonce": 359281307, + "isDeleted": false, + "boundElements": null, + "updated": 1764875186373, + "link": null, + "locked": false, + "text": "/anonymizer/anonymi\nze-document", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "a9YgxexRbVJcxPwYgreZn", + "originalText": "/anonymizer/anonymize-document", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "h9PsSXNcnOUBkMSzBjg6z", + "type": "rectangle", + "x": 1052.940182148664, + "y": 4308.408197109689, + "width": 215.66666666666663, + "height": 280.0447761194029, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b08", + "roundness": { + "type": 3 + }, + "seed": 1300012667, + "version": 142, + "versionNonce": 1041008603, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false + }, + { + "id": "UieOrbihjMKgv6n7yxaDj", + "type": "line", + "x": 1096.3954060292608, + "y": 4348.64451551765, + "width": 94.95771144278605, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b09", + "roundness": { + "type": 2 + }, + "seed": 720967451, + "version": 110, + "versionNonce": 1216664699, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 94.95771144278605, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "10owweBOlc7qCyP6aRJJV", + "type": "line", + "x": 1092.0153180830398, + "y": 4375.792065073328, + "width": 48.28358208955214, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0A", + "roundness": { + "type": 2 + }, + "seed": 1604156347, + "version": 213, + "versionNonce": 1133153563, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.28358208955214, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "BQmhv6TG0JxjRxuwDfSMw", + "type": "line", + "x": 1092.561879853752, + "y": 4405.564643767066, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0B", + "roundness": { + "type": 2 + }, + "seed": 395070555, + "version": 243, + "versionNonce": 311439803, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "4Hm0xXA1DdypSLAR9BoaN", + "type": "text", + "x": 929.1926219227548, + "y": 4635.127102582326, + "width": 436.57354736328125, + "height": 40.2363184079602, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0C", + "roundness": null, + "seed": 1452804347, + "version": 162, + "versionNonce": 112738907, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "text": "documento_anonimizado.odt", + "fontSize": 32.189054726368155, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "documento_anonimizado.odt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "5LyI3ky8p3RebAfVu1tA_", + "type": "line", + "x": 1091.5670478203056, + "y": 4433.945510542525, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0D", + "roundness": { + "type": 2 + }, + "seed": 37958043, + "version": 141, + "versionNonce": 685063931, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "OSeMl_IH1Pq_pZxGzfw_4", + "type": "line", + "x": 1098.7315886444599, + "y": 4460.844384416316, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0E", + "roundness": { + "type": 2 + }, + "seed": 734021179, + "version": 175, + "versionNonce": 1130997659, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "b4ihNsBPXcvY1lhr6-Ohh", + "type": "line", + "x": 1099.084131656272, + "y": 4486.3503160727, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0F", + "roundness": { + "type": 2 + }, + "seed": 435275483, + "version": 281, + "versionNonce": 749206587, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "mq_eSKsQgMs7MFsq2AJNk", + "type": "line", + "x": 1098.0892996228258, + "y": 4514.731182848159, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0G", + "roundness": { + "type": 2 + }, + "seed": 325727099, + "version": 179, + "versionNonce": 1349696731, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "YN6yT1TNVtadNJ1X5Melr", + "type": "line", + "x": 1105.2538404469801, + "y": 4541.630056721949, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "SQGLLXLdvVhMNPoAROpAt" + ], + "frameId": null, + "index": "b0H", + "roundness": { + "type": 2 + }, + "seed": 1751911451, + "version": 213, + "versionNonce": 2052876667, + "isDeleted": false, + "boundElements": [], + "updated": 1764875186373, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "9G3g0qab8TQWTTo68WKEc", + "type": "text", + "x": 1536.304106606377, + "y": 329.64285714285705, + "width": 476.1529541015625, + "height": 283.3333333333333, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0L", + "roundness": null, + "seed": 915516597, + "version": 11, + "versionNonce": 672956187, + "isDeleted": false, + "boundElements": null, + "updated": 1764875143575, + "link": null, + "locked": false, + "text": "{\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n ...\n }\n }\n ]\n}", + "fontSize": 16.19047619047619, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "{\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n ...\n }\n }\n ]\n}", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "RCTW7-Twmc0vzPbjJZlva", + "type": "rectangle", + "x": 629.1746031746031, + "y": 2613.596856218215, + "width": 1090.833333333333, + "height": 576.5587918015109, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0N", + "roundness": { + "type": 3 + }, + "seed": 2097245493, + "version": 450, + "versionNonce": 492327899, + "isDeleted": false, + "boundElements": [ + { + "id": "3VriZd7FuEOwMpdPbnIiX", + "type": "arrow" + } + ], + "updated": 1764875205688, + "link": null, + "locked": false + }, + { + "id": "Hx_F-eFq9Elf3olyEAh5H", + "type": "text", + "x": 791.7764842274424, + "y": 2644.4357759284935, + "width": 711.4058227539062, + "height": 516.4285714285714, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0O", + "roundness": null, + "seed": 1957523003, + "version": 210, + "versionNonce": 1767565467, + "isDeleted": false, + "boundElements": [ + { + "id": "3VriZd7FuEOwMpdPbnIiX", + "type": "arrow" + } + ], + "updated": 1764875208055, + "link": null, + "locked": false, + "text": "[\n {\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n \"aymurai_label_suffix\": 1,\n \"canonical_entity_id\": ,\n ...\n }\n }\n ]\n }\n]", + "fontSize": 22.952380952380953, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[\n {\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n \"aymurai_label_suffix\": 1,\n \"canonical_entity_id\": ,\n ...\n }\n }\n ]\n }\n]", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "M3_9cSeevpvgvuGOJmtLu", + "type": "rectangle", + "x": 341.0753968253973, + "y": -946.5918028903099, + "width": 215.66666666666663, + "height": 280.0447761194029, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0P", + "roundness": { + "type": 3 + }, + "seed": 630737269, + "version": 231, + "versionNonce": 857423573, + "isDeleted": false, + "boundElements": [ + { + "id": "EOtxLYz_c5dBLBZTsf6bP", + "type": "arrow" + } + ], + "updated": 1764875361089, + "link": null, + "locked": false + }, + { + "id": "e0VYlO1HBUE6Y61hMmjQS", + "type": "line", + "x": 384.5306207059946, + "y": -906.3554844823498, + "width": 94.95771144278605, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0Q", + "roundness": { + "type": 2 + }, + "seed": 924406485, + "version": 197, + "versionNonce": 1258245525, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 94.95771144278605, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "zz4q6sGFYLSQEGm4_Z_pZ", + "type": "line", + "x": 380.15053275977357, + "y": -879.2079349266717, + "width": 48.28358208955214, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0R", + "roundness": { + "type": 2 + }, + "seed": 2054303797, + "version": 300, + "versionNonce": 2102055669, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.28358208955214, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "S9t8FU85SfKbOpC8RR_gM", + "type": "line", + "x": 380.6970945304854, + "y": -849.4353562329334, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0S", + "roundness": { + "type": 2 + }, + "seed": 227041685, + "version": 330, + "versionNonce": 1522445397, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "DL-iPq6d9xxkjVSnghr83", + "type": "text", + "x": 394.1873371239051, + "y": -619.8728974176731, + "width": 82.85454631444826, + "height": 80.47263681592038, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0T", + "roundness": null, + "seed": 898721525, + "version": 211, + "versionNonce": 53500341, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "text": ".docx\n.pdf", + "fontSize": 32.189054726368155, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": ".docx\n.pdf", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "eb7-zegRUYIJqYobn0GTg", + "type": "line", + "x": 379.7022624970393, + "y": -821.0544894574741, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0U", + "roundness": { + "type": 2 + }, + "seed": 1964163157, + "version": 228, + "versionNonce": 2143640341, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "76_13jw7FNEf9CD3vN4K7", + "type": "line", + "x": 386.86680332119363, + "y": -794.1556155836837, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0V", + "roundness": { + "type": 2 + }, + "seed": 1642196405, + "version": 262, + "versionNonce": 418590837, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "7bpvHSILjOEGoxBVuVvmL", + "type": "line", + "x": 387.21934633300566, + "y": -768.6496839272999, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0W", + "roundness": { + "type": 2 + }, + "seed": 992243477, + "version": 368, + "versionNonce": 1820285397, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "hunvhsGsscndVNFWE5PbJ", + "type": "line", + "x": 386.2245142995596, + "y": -740.2688171518405, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0X", + "roundness": { + "type": 2 + }, + "seed": 1062192245, + "version": 266, + "versionNonce": 1132418869, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "T8wwR2IwhMmplgY6InC65", + "type": "line", + "x": 393.3890551237139, + "y": -713.3699432780502, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0Y", + "roundness": { + "type": 2 + }, + "seed": 1110011349, + "version": 300, + "versionNonce": 676898965, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "EOtxLYz_c5dBLBZTsf6bP", + "type": "arrow", + "x": 560.3079384765335, + "y": -801.9441674997673, + "width": 311.1087281901333, + "height": 3.0858479987831515, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0Z", + "roundness": { + "type": 2 + }, + "seed": 46590773, + "version": 818, + "versionNonce": 1436837659, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "AQOsTPNecUVIP5dcYG-CL" + } + ], + "updated": 1764875651472, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 311.1087281901333, + 3.0858479987831515 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "M3_9cSeevpvgvuGOJmtLu", + "focus": 0.024877658056798795, + "gap": 5.80129262390335 + }, + "endBinding": { + "elementId": "vWBP3oXNVHZGwRqV71dye", + "focus": 0.16647678467610177, + "gap": 26.273809523809405 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "AQOsTPNecUVIP5dcYG-CL", + "type": "text", + "x": 617.1124170125182, + "y": -797.1021624155233, + "width": 164.99977111816406, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0a", + "roundness": null, + "seed": 1099807893, + "version": 47, + "versionNonce": 156055285, + "isDeleted": false, + "boundElements": [], + "updated": 1764875337505, + "link": null, + "locked": false, + "text": "/misc/document-\nextract", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "EOtxLYz_c5dBLBZTsf6bP", + "originalText": "/misc/document-extract", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "vWBP3oXNVHZGwRqV71dye", + "type": "text", + "x": 897.6904761904764, + "y": -891.8596681096677, + "width": 536.5656565656566, + "height": 232.7272727272728, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0b", + "roundness": null, + "seed": 372466165, + "version": 414, + "versionNonce": 1404522139, + "isDeleted": false, + "boundElements": [ + { + "id": "EOtxLYz_c5dBLBZTsf6bP", + "type": "arrow" + } + ], + "updated": 1764875606006, + "link": null, + "locked": false, + "text": "{\n \"document_id\": ,\n \"document\": [\n \"R., MATIAS EZEQUIEL SOBRE\n128 1 párr\",\n ...\n ]\n}", + "fontSize": 23.27272727272728, + "fontFamily": 8, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{\n \"document_id\": ,\n \"document\": [\n \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n ...\n ]\n}", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "AJupGiIgf295eDXNF1NTl", + "type": "rectangle", + "x": 1911.6785714285718, + "y": -959.9404761904758, + "width": 521.6666666666664, + "height": 428.88888888888886, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0e", + "roundness": { + "type": 3 + }, + "seed": 807684629, + "version": 367, + "versionNonce": 1386791739, + "isDeleted": false, + "boundElements": [ + { + "id": "hotXOq7-8juh39kLJem4p", + "type": "arrow" + } + ], + "updated": 1764875606006, + "link": null, + "locked": false + }, + { + "id": "uIFK9kOYNJE4xD48qvRxR", + "type": "text", + "x": 2083.5527161189484, + "y": -757.9960317460313, + "width": 127.13986206054688, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0f", + "roundness": null, + "seed": 1570520949, + "version": 331, + "versionNonce": 2091546331, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "text": "NER outputs", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "NER outputs", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "InHz3D1JmEtzd_JC59uqm", + "type": "text", + "x": 1934.4354277111238, + "y": -887.162698412698, + "width": 476.1529541015625, + "height": 283.3333333333333, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0g", + "roundness": null, + "seed": 1999538389, + "version": 169, + "versionNonce": 660586261, + "isDeleted": false, + "boundElements": [], + "updated": 1764875581978, + "link": null, + "locked": false, + "text": "{\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n ...\n }\n }\n ]\n}", + "fontSize": 16.19047619047619, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "{\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n ...\n }\n }\n ]\n}", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "OKZB0eDL-ABRXZHWhREn2", + "type": "image", + "x": 2663.960317460318, + "y": -976.9960317460313, + "width": 1024, + "height": 473, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0h", + "roundness": null, + "seed": 1317474869, + "version": 341, + "versionNonce": 543979707, + "isDeleted": false, + "boundElements": [ + { + "id": "hotXOq7-8juh39kLJem4p", + "type": "arrow" + } + ], + "updated": 1764875603696, + "link": null, + "locked": false, + "status": "pending", + "fileId": "7828d57068931cdd86458a8cbc891ed6ee122d38", + "scale": [ + 1, + 1 + ], + "crop": null + }, + { + "id": "lhZO6MPRRTWy3V55I0vZM", + "type": "arrow", + "x": 3756.6202534941517, + "y": -758.447755452392, + "width": 403.8000183105464, + "height": 5.908527289127619, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0i", + "roundness": { + "type": 2 + }, + "seed": 280036245, + "version": 1548, + "versionNonce": 1898165275, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "GHolMdYEXJBOSjiLEJF7i" + } + ], + "updated": 1764875603696, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 195.91698365823413, + -2.048276293639333 + ], + [ + 403.8000183105464, + -5.908527289127619 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "GHolMdYEXJBOSjiLEJF7i", + "type": "text", + "x": 3760.5369624941827, + "y": -745.4960317460313, + "width": 264.00054931640625, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0j", + "roundness": null, + "seed": 1331022069, + "version": 47, + "versionNonce": 2111430683, + "isDeleted": false, + "boundElements": [], + "updated": 1764875595533, + "link": null, + "locked": false, + "text": "/anonymizer/anonymize-document", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lhZO6MPRRTWy3V55I0vZM", + "originalText": "/anonymizer/anonymize-document", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "H45OT-nnyrYBIdX_fHnB-", + "type": "rectangle", + "x": 4251.421104004587, + "y": -923.9736436863299, + "width": 215.66666666666663, + "height": 280.0447761194029, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0k", + "roundness": { + "type": 3 + }, + "seed": 1543168597, + "version": 364, + "versionNonce": 1947781947, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false + }, + { + "id": "HkLFBBIeoT2NmdHolQogn", + "type": "line", + "x": 4294.876327885184, + "y": -883.7373252783694, + "width": 94.95771144278605, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0l", + "roundness": { + "type": 2 + }, + "seed": 997576629, + "version": 330, + "versionNonce": 787590107, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 94.95771144278605, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "dP23wQQAJGa8EbTko6TLk", + "type": "line", + "x": 4290.496239938962, + "y": -856.5897757226916, + "width": 48.28358208955214, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0m", + "roundness": { + "type": 2 + }, + "seed": 1005326613, + "version": 433, + "versionNonce": 690321531, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.28358208955214, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "AzAxanw4nR0bo9R-OtYUW", + "type": "line", + "x": 4291.042801709675, + "y": -826.8171970289535, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0n", + "roundness": { + "type": 2 + }, + "seed": 1255351925, + "version": 463, + "versionNonce": 276214043, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "cPvNsqjYFBYhf1dvqCfUM", + "type": "text", + "x": 4127.673543778677, + "y": -597.254738213693, + "width": 436.57354736328125, + "height": 40.2363184079602, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0o", + "roundness": null, + "seed": 183537621, + "version": 382, + "versionNonce": 911726011, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "text": "documento_anonimizado.odt", + "fontSize": 32.189054726368155, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "documento_anonimizado.odt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "FZMsE2ftxJlqlXy5y1Kot", + "type": "line", + "x": 4290.047969676229, + "y": -798.4363302534942, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0p", + "roundness": { + "type": 2 + }, + "seed": 710528309, + "version": 361, + "versionNonce": 434857563, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "c5LayQF2_U6-IqmzQigBO", + "type": "line", + "x": 4297.212510500382, + "y": -771.5374563797035, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0q", + "roundness": { + "type": 2 + }, + "seed": 2020892309, + "version": 395, + "versionNonce": 839115515, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "CZP0a-w2xzB-aIeBIdTfj", + "type": "line", + "x": 4297.565053512195, + "y": -746.0315247233196, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0r", + "roundness": { + "type": 2 + }, + "seed": 1005962229, + "version": 501, + "versionNonce": 1998834587, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "YGpIXuZRgXAXrbeyTS72v", + "type": "line", + "x": 4296.570221478749, + "y": -717.6506579478604, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0s", + "roundness": { + "type": 2 + }, + "seed": 1222868309, + "version": 399, + "versionNonce": 763207739, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "ABZSKgAIt_rm_gemO06m9", + "type": "line", + "x": 4303.734762302903, + "y": -690.7517840740705, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "heHx6O1UXsEtwJkRBikSl" + ], + "frameId": null, + "index": "b0t", + "roundness": { + "type": 2 + }, + "seed": 1452477109, + "version": 433, + "versionNonce": 1861476571, + "isDeleted": false, + "boundElements": [], + "updated": 1764875579057, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "hotXOq7-8juh39kLJem4p", + "type": "arrow", + "x": 2444.591377430666, + "y": -762.2243671485597, + "width": 196.3689400296521, + "height": 3.692569021604527, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0u", + "roundness": { + "type": 2 + }, + "seed": 755633851, + "version": 323, + "versionNonce": 1071716059, + "isDeleted": false, + "boundElements": null, + "updated": 1764875655966, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 196.3689400296521, + -3.692569021604527 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "AJupGiIgf295eDXNF1NTl", + "focus": 0.03289811846175117, + "gap": 14.626683917308128 + }, + "endBinding": { + "elementId": "OKZB0eDL-ABRXZHWhREn2", + "focus": 0.06765961386393643, + "gap": 23 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "gXsFYxTEKv14jMJGLJI8O", + "type": "arrow", + "x": 1452.931012958739, + "y": -762.3018491588567, + "width": 403.8000183105464, + "height": 5.908527289127619, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0v", + "roundness": { + "type": 2 + }, + "seed": 1974610101, + "version": 1579, + "versionNonce": 919034907, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Q4IQFcmT8i1SnTx5DuzTS" + } + ], + "updated": 1764875655966, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 195.91698365823413, + -2.048276293639333 + ], + [ + 403.8000183105464, + -5.908527289127619 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "Q4IQFcmT8i1SnTx5DuzTS", + "type": "text", + "x": 1566.9144893334446, + "y": -809.3501254524961, + "width": 167.20034790039062, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0w", + "roundness": null, + "seed": 2134198805, + "version": 55, + "versionNonce": 1592755067, + "isDeleted": false, + "boundElements": [], + "updated": 1764875655966, + "link": null, + "locked": false, + "text": "/anonymizer/predict", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "gXsFYxTEKv14jMJGLJI8O", + "originalText": "/anonymizer/predict", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "jFnC8PtgWHvjc9weWuFWX", + "type": "text", + "x": 326.69940776522253, + "y": -1119.4404761904757, + "width": 654.3769721137143, + "height": 76.99999999999989, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0z", + "roundness": null, + "seed": 29900859, + "version": 85, + "versionNonce": 988358325, + "isDeleted": false, + "boundElements": null, + "updated": 1764875700204, + "link": null, + "locked": false, + "text": "Anonymization pipeline", + "fontSize": 61.59999999999989, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Anonymization pipeline", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "hdfluh6k2tthpQT1_UP4m", + "type": "text", + "x": 326.69940776522253, + "y": -3.9404761904756924, + "width": 1170.5897216796875, + "height": 76.99999999999986, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b10", + "roundness": null, + "seed": 2091673109, + "version": 149, + "versionNonce": 725184187, + "isDeleted": false, + "boundElements": [], + "updated": 1764875707681, + "link": null, + "locked": false, + "text": "Anonymization + disambiguation pipeline", + "fontSize": 61.59999999999989, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Anonymization + disambiguation pipeline", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": { + "7828d57068931cdd86458a8cbc891ed6ee122d38": { + "mimeType": "image/png", + "id": "7828d57068931cdd86458a8cbc891ed6ee122d38", + "dataURL": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAHZCAYAAAAYFEnDAAAAAXNSR0IArs4c6QAAIABJREFUeF7snQd4FcXXxt+9JZUUQhJC6BhaKCqgVAUEQSxIL6KoIKAiKEqRooQiRRAUQSlW9K8IAgICChYEAREsIERASuiQhJDe7r2733cmd8Nm2ZvckISWs8/DE5K7d3fmN7Ozc94554wEPpgAE2ACTIAJMAEmwASYABNgAkyACTCBW56AdMvXkCvIBJgAE2ACTIAJMAEmwASYABNgAkyACYAFAO4ETIAJMAEmwASYABNgAkyACTABJsAESgEBFgBKQSNzFZkAE2ACTIAJMAEmwASYABNgAkyACbAAwH2ACTABJsAEmAATYAJMgAkwASbABJhAKSDAAkApaGSuIhNgAkyACTABJsAEmAATYAJMgAkwARYAuA8wASbABJgAE2ACTIAJMAEmwASYABMoBQRYACgFjcxVZAJMgAkwASbABJgAE2ACTIAJMAEmwAIA9wEmwASYABNgAkyACTABJsAEmAATYAKlgAALAKWgkbmKTIAJMAEmwASYABNgAkyACTABJsAEWADgPsAEmAATYAJMgAkwASbABJgAE2ACTKAUEGABoBQ0MleRCTABJsAEmAATYAJMgAkwASbABJgACwDcB5gAE2ACTIAJMAEmwASYABNgAkyACZQCAiwAlIJG5ioyASbABJgAE2ACTIAJMAEmwASYABNgAYD7ABNgAkyACTABJsAEmAATYAJMgAkwgVJAgAWAUtDIXEUmwASYABNgAkyACTABJsAEmAATYAIsAHAfYAJMgAkwASbABJgAE2ACTIAJMAEmUAoIsABQChqZq8gEmAATYAJMgAkwASbABJgAE2ACTIAFAO4DTIAJMAEmwASYABNgAkyACTABJsAESgEBFgBKQSNzFZkAE2ACTIAJMAEmwASYABNgAkyACbAAwH2ACTABJsAEmAATYAJMgAkwASbABJhAKSDAAkApaGSuIhNgAkyACTABJsAEmAATYAJMgAkwARYAuA8wASbABJgAE2ACTIAJMAEmwASYABMoBQRYACgFjcxVZAJMgAkwASbABJgAE2ACTIAJMAEmcDMIACajZlIUBZJ0ufj0Ox3q3wr6nM7Vf0d/H/UaUVFRmDhx4hXX1t9DvSaVQb32pEmT8nxXf462Dtr7G5WfrkVl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", + "created": 1764873715847, + "lastRetrieved": 1764873715847 + } + } +} \ No newline at end of file From 29b608247b50e7e283a13a789c6badfef05db8f0 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 5 Dec 2025 19:31:33 +0000 Subject: [PATCH 023/101] =?UTF-8?q?=F0=9F=9A=A7=20WIP:=20Add=20Playwright?= =?UTF-8?q?=20PJN=20scraper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 1 + scraper/README.md | 97 +++++ scraper/playwright_scraper.py | 739 ++++++++++++++++++++++++++++++++++ uv.lock | 35 +- 4 files changed, 871 insertions(+), 1 deletion(-) create mode 100644 scraper/README.md create mode 100644 scraper/playwright_scraper.py diff --git a/pyproject.toml b/pyproject.toml index b89395ab..26c99114 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,6 +117,7 @@ dev = [ "pip>=24.3.1", "streamlit>=1.21.0", "dspy>=3.0.4", + "playwright==1.56.0" ] [tool.setuptools.packages.find] diff --git a/scraper/README.md b/scraper/README.md new file mode 100644 index 00000000..dc965075 --- /dev/null +++ b/scraper/README.md @@ -0,0 +1,97 @@ +# Scraper Playwright - Gestión Documental PJN + +Este repositorio contiene un script en Python que usa **Playwright** para automatizar la recolección +de documentos PDF publicados en la sección de gestión documental del Poder Judicial de la Nación +(https://www.pjn.gov.ar/gestion-documental). El scraper reproduce las llamadas AJAX que genera el front-end, +mantiene las cookies que requiere el API y descarga los adjuntos desde el servicio oficial. + +## Requisitos + +1. Python 3.8+ +2. Playwright y el navegador Chromium. + +```bash +python -m pip install --upgrade pip +pip install playwright +playwright install chromium +``` + +## Uso básico + +```bash +python playwright_scraper.py --output-dir datos --max-pages 5 --per-page 50 --pdf-template "https://pjn-documento-api.pjn.gov.ar/api/documento/adjunto/{id}" +``` + +El script hará: + +- Abrirá una sesión de Playwright y visitará la página oficial. +- Detectará automáticamente qué llamada REST obtiene los documentos. +- Iterará páginas y descargará los PDFs dentro del directorio `datos`. + +## Aplicar filtros desde la línea de comandos + +La interfaz web permite limitar por dependencia, categorías y rango de fechas. El scraper replica ese comportamiento con los argumentos: + +- `--dependencia "Fueros Federales"` aplica ese texto tanto a `dependencia` como a `dependenciaTexto`. +- `--category` se puede repetir para simular clics en categorías/subcategorías anidadas (por ejemplo `--category Reglamentos --category Normativa`). +- `--desde` y `--hasta` definen los campos `fechaDesde`/`fechaHasta` (o `desde`/`hasta`) que el backend espera. +- `--filter clave=valor` te deja añadir cualquier parámetro extra (por ejemplo `--filter orden=desc`, `--filter tipoDocumento=Resolución`). +- `--simulate-ui` simula los clicks sobre filtros, expande los botones “Mostrar más” y rellena las fechas como lo hace la UI antes de capturar la llamada AJAX. Úsalo si necesitás replicar exactamente la interacción de navegación para los filtros que ves en pantalla. +- `--debug-ui` habilita registros detallados sobre cada intento de clic/expansión para que puedas ver en consola qué filtros se están tocando en tiempo real. + +Los filtros se mezclan automáticamente en la query y en el cuerpo de la petición, así que puedes combinar varios `-f` en el mismo comando. + +## Estructura de almacenamiento guiada por filtros + +Los documentos descargados se organizan siguiendo los filtros aplicados y la metadata del registro: + +- Cada PDF se guarda en `ruta-de-salida///`, usando la dependencia que especifiques o la que entrega el API. +- La categoría se extrae de campos como `rubro`, `categoria` o `tipo` cuando están presentes. +- Si pasás `--category` el scraper usa esa secuencia de categorías/subcategorías como niveles adicionales (en lugar de los valores devueltos por el API), lo que refleja la navegación que aplicaste en los filtros. +- Si falta dependencia o año, se usan nombres como `sin-dependencia` o `sin-anio` para evitar colisiones. +- Los nombres de archivo ahora conservan acentos y caracteres especiales porque la normalización usa Unicode completo (`NFC`) antes de reemplazar espacios; así el título original permanece íntegro para tu pipeline de metadata. + +Esto facilita tener directorios alineados con las dependencias del Poder Judicial y continuar el pipeline de anonimización con una estructura consistente. + +Puedes ajustar el número de páginas, el retraso entre peticiones o el límite de documentos con +`--delay`, `--max-documents`, `--max-pages`. + +## Firma manual del API + +Si lo prefieres, puedes evitar la detección automática y proporcionar un JSON con la firma de las peticiones. +Esto es útil cuando ya sabes qué endpoint se usa o necesitas definir filtros específicos. + +```json +{ + "method": "POST", + "base_url": "https://www.pjn.gov.ar/api/documento/paginado", + "headers": { + "Accept": "application/json", + "Referer": "https://www.pjn.gov.ar/gestion-documental" + }, + "query_params": { + "orden": "desc" + }, + "json_body": { + "pagina": 0, + "size": 25, + "dependencia": "Fueros Federales" + }, + "listing_path": ["data", "registros"], + "page_keys": { + "pagina": 0 + }, + "size_keys": { + "size": 25 + }, + "body_type": "json" +} +``` + +Guarda ese JSON y pásalo al script con `--signature-file firma.json`. + +## Siguientes pasos + +1. Define reglas de nombrado y almacenamiento según tu pipeline de anonimización. +2. Agrega paralelismo/control de errores si necesitas escalar la descarga. +3. Encadena el dump resultante con los módulos de extracción y validación de Aymurai. diff --git a/scraper/playwright_scraper.py b/scraper/playwright_scraper.py new file mode 100644 index 00000000..f108563e --- /dev/null +++ b/scraper/playwright_scraper.py @@ -0,0 +1,739 @@ +#!/usr/bin/env python3 +""" +Playwright scraper para la sección “Gestión Documental” del Poder Judicial de la Nación. + +Este script usa Playwright para capturar las cabeceras y cookies que el frontend genera +y después reproduce las llamadas al API para descargar en lote los PDFs que se enumeran +en la interfaz. Se detecta de forma dinámica el endpoint y los parámetros de paginación, +pero también puede recibir una firma manual si así se prefiere. +""" + +import argparse +import asyncio +import copy +import json +import logging +import re +import unicodedata +import urllib.parse +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, Iterable, List, Optional, Tuple + +from playwright.async_api import APIRequestContext, Page, async_playwright + +logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s] %(levelname)s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) +logger = logging.getLogger(__name__) + + +PAGE_KEYWORDS = ( + "pagina", + "page", + "pageindex", + "pagenumber", + "paginaactual", + "numeropagina", + "offset", +) +SIZE_KEYWORDS = ( + "size", + "pagesize", + "tamanio", + "cantidad", + "limit", + "perpage", + "per_page", +) + +MORE_BUTTON_SELECTOR = "text=/Mostrar\\s+m[aá]s/i" + + +def safe_filename(value: str) -> str: + clean = unicodedata.normalize("NFC", value.strip()) + clean = re.sub(r"\s+", "-", clean) + clean = re.sub(r"[^\w\-\.]", "", clean) + return clean[:120] + + +def detect_numeric_fields( + container: Dict[str, Any], keywords: Iterable[str] +) -> Dict[str, int]: + matches: Dict[str, int] = {} + for key, value in container.items(): + lowered = key.lower() + if any(keyword in lowered for keyword in keywords): + try: + matches[key] = int(value) + except (TypeError, ValueError): + continue + return matches + + +def update_values( + container: Dict[str, Any], keys: Iterable[str], new_value: int +) -> None: + for key in keys: + if key in container: + container[key] = new_value + + +def strip_sensitive_headers(headers: Dict[str, str]) -> Dict[str, str]: + blocked = { + "content-length", + "origin", + "connection", + "accept-encoding", + } + return {k: v for k, v in headers.items() if k.lower() not in blocked} + + +def load_signature_from_file(path: Path) -> Dict[str, Any]: + with path.open("r", encoding="utf-8") as fh: + return json.load(fh) + + +def extract_attachment_id(record: Dict[str, Any]) -> Optional[str]: + candidates = ( + "idAdjunto", + "adjunto", + "idDocumento", + "documentoAdjunto", + "id", + "adjuntoId", + ) + for key in candidates: + value = record.get(key) + if isinstance(value, dict): + nested = value.get("id") or value.get("codigo") + if nested: + return str(nested) + elif value: + return str(value) + return None + + +def record_field(record: Dict[str, Any], keys: Iterable[str]) -> Optional[str]: + for key in keys: + if key in record and record[key]: + return str(record[key]) + return None + + +def find_listing_path( + payload: Any, path: Tuple[str, ...] = () +) -> Optional[Tuple[Tuple[str, ...], List[Dict[str, Any]]]]: + if isinstance(payload, dict): + for key, value in payload.items(): + if ( + isinstance(value, list) + and value + and all(isinstance(item, dict) for item in value) + ): + if any( + any( + "id" in field.lower() or "adjunto" in field.lower() + for field in item.keys() + ) + for item in value + ): + return path + (key,), value + child = find_listing_path(value, path + (key,)) + if child: + return child + elif isinstance(payload, list): + if payload and all(isinstance(item, dict) for item in payload): + if any( + any( + "id" in field.lower() or "adjunto" in field.lower() + for field in item.keys() + ) + for item in payload + ): + return path, payload + return None + + +def extract_records_by_path( + payload: Dict[str, Any], path: Tuple[str, ...] +) -> List[Dict[str, Any]]: + current: Any = payload + for key in path: + if not isinstance(current, dict): + return [] + current = current.get(key) + if current is None: + return [] + if isinstance(current, list): + return [item for item in current if isinstance(item, dict)] + return [] + + +def derive_pdf_url(template: str, attachment_id: str) -> str: + if "{id}" in template: + return template.format(id=attachment_id) + return template.rstrip("/") + "/" + attachment_id + + +@dataclass +class SearchSignature: + method: str + base_url: str + headers: Dict[str, str] + query_params: Dict[str, str] + json_body: Optional[Dict[str, Any]] + listing_path: Tuple[str, ...] + example_record: Dict[str, Any] + page_keys: Dict[str, int] + size_keys: Dict[str, int] + body_type: str + + +def build_signature_from_request( + request, + listing_path: Tuple[str, ...], + example_record: Dict[str, Any], + response_payload: Dict[str, Any], +) -> SearchSignature: + parsed = urllib.parse.urlparse(request.url) + base_url = urllib.parse.urlunparse(parsed._replace(query="")) + query_params = dict(urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)) + method = request.method + headers = strip_sensitive_headers(dict(request.headers)) + json_body: Optional[Dict[str, Any]] = None + post_data = request.post_data + content_type = request.headers.get("content-type", "") + if post_data: + if "x-www-form-urlencoded" in content_type: + json_body = dict(urllib.parse.parse_qsl(post_data)) + else: + try: + json_body = json.loads(post_data) + except json.JSONDecodeError: + logger.debug("No se pudo parsear cuerpo JSON de la petición inicial.") + initial_container = {} + initial_container.update(query_params) + if isinstance(json_body, dict): + initial_container.update(json_body) + page_keys = detect_numeric_fields(initial_container, PAGE_KEYWORDS) + size_keys = detect_numeric_fields(initial_container, SIZE_KEYWORDS) + if not listing_path: + found = find_listing_path(response_payload) + if not found: + raise RuntimeError( + "No se pudo determinar la ruta de listado de documentos." + ) + listing_path = found[0] + if post_data: + if "x-www-form-urlencoded" in content_type: + body_type = "form" + else: + body_type = "json" + else: + body_type = "none" + return SearchSignature( + method=method.upper(), + base_url=base_url, + headers=headers, + query_params=query_params, + json_body=json_body, + listing_path=listing_path, + example_record=example_record, + page_keys=page_keys, + size_keys=size_keys, + body_type=body_type, + ) + + +async def capture_signature( + page: Page, args: argparse.Namespace, timeout: float = 20.0 +) -> SearchSignature: + future = asyncio.get_running_loop().create_future() + + async def inspect_response(response): + if future.done(): + return + if response.status != 200: + return + if response.request.resource_type != "xhr": + return + content_type = response.headers.get("content-type", "") + if "application/json" not in content_type: + return + try: + payload = await response.json() + except Exception: + return + found = find_listing_path(payload) + if not found: + return + records = found[1] + if not records: + return + future.set_result((response.request, found[0], records[0], payload)) + + page.on( + "response", + lambda response: asyncio.create_task(inspect_response(response)), + ) + await page.goto( + "https://www.pjn.gov.ar/gestion-documental", wait_until="networkidle" + ) + if args.simulate_ui: + try: + await apply_ui_filters(page, args) + except Exception as exc: # pragma: no cover - best effort + logger.warning("Error al aplicar filtros UI: %s", exc) + try: + await asyncio.wait_for(future, timeout=timeout) + except asyncio.TimeoutError: + logger.warning( + "No se detectó automáticamente la llamada al API. Asegúrate de navegar en la página." + ) + raise + + request, listing_path, sample_record, payload = future.result() + signature = build_signature_from_request( + request, listing_path, sample_record, payload + ) + logger.info("Firma detectada: %s %s", signature.method, signature.base_url) + logger.debug("Campos página: %s", signature.page_keys) + logger.debug("Campos tamaño: %s", signature.size_keys) + return signature + + +def load_signature(source: Dict[str, Any]) -> SearchSignature: + signature = SearchSignature( + method=source["method"].upper(), + base_url=source["base_url"], + headers=source.get("headers", {}), + query_params=source.get("query_params", {}), + json_body=source.get("json_body"), + listing_path=tuple(source["listing_path"]), + example_record=source.get("example_record", {}), + page_keys={k: int(v) for k, v in source.get("page_keys", {}).items()}, + size_keys={k: int(v) for k, v in source.get("size_keys", {}).items()}, + body_type=source.get("body_type", "json"), + ) + return signature + + +def prepare_page_payload( + signature: SearchSignature, + page_value: int, + page_size: int, +) -> Tuple[Dict[str, str], Optional[Dict[str, Any]]]: + query_params = signature.query_params.copy() + if signature.json_body is not None: + body_payload = copy.deepcopy(signature.json_body) + else: + body_payload = None + if signature.page_keys: + update_values(query_params, signature.page_keys.keys(), page_value) + if body_payload is not None: + update_values(body_payload, signature.page_keys.keys(), page_value) + if signature.size_keys: + update_values(query_params, signature.size_keys.keys(), page_size) + if body_payload is not None: + update_values(body_payload, signature.size_keys.keys(), page_size) + return query_params, body_payload + + +def apply_filter_overrides( + query_params: Dict[str, str], + body_payload: Optional[Dict[str, Any]], + overrides: Dict[str, str], +) -> None: + if not overrides: + return + for key, value in overrides.items(): + query_params[key] = value + if body_payload is not None: + body_payload[key] = value + + +def build_filter_overrides( + raw_filters: Iterable[str], + dependencia: Optional[str], + desde: Optional[str], + hasta: Optional[str], +) -> Dict[str, str]: + overrides: Dict[str, str] = {} + for option in raw_filters: + if "=" not in option: + raise ValueError(f"Filtro inválido: {option!r}. Use clave=valor.") + key, value = option.split("=", 1) + overrides[key] = value + if dependencia: + overrides.setdefault("dependencia", dependencia) + overrides.setdefault("dependenciaTexto", dependencia) + if desde: + overrides.setdefault("fechaDesde", desde) + overrides.setdefault("desde", desde) + if hasta: + overrides.setdefault("fechaHasta", hasta) + overrides.setdefault("hasta", hasta) + return overrides + + +def ui_debug(args: argparse.Namespace, message: str) -> None: + if getattr(args, "debug_ui", False): + logger.info("[UI] %s", message) + + +async def expand_more_filters(page: Page, args: argparse.Namespace) -> None: + while True: + locator = page.locator(MORE_BUTTON_SELECTOR) + if not await locator.count(): + break + ui_debug(args, "Expandiendo filtros con 'Mostrar más'") + await locator.first.click() + await page.wait_for_timeout(250) + + +async def click_filter_option(page: Page, args: argparse.Namespace, text: str) -> bool: + text = text.strip() + if not text: + return False + strategies = [ + ("role=button", page.get_by_role("button", name=text, exact=False)), + ("role=link", page.get_by_role("link", name=text, exact=False)), + ("text-match", page.get_by_text(text, exact=False)), + ("button:has-text", page.locator(f'button:has-text("{text}")')), + ("div:has-text", page.locator(f'div:has-text("{text}")')), + ] + ui_debug( + args, + f"Intentando seleccionar filtro '{text}' usando {len(strategies)} estrategias", + ) + for description, locator in strategies: + ui_debug(args, f"Probando estrategia '{description}' para '{text}'") + if await locator.count(): + ui_debug(args, f"Clic en '{text}' usando '{description}'") + await locator.first.click() + await page.wait_for_timeout(400) + return True + ui_debug(args, f"No se encontró '{text}' con '{description}'") + return False + + +def normalize_ui_date(value: str) -> str: + try: + parsed = datetime.fromisoformat(value) + return parsed.strftime("%d/%m/%Y") + except ValueError: + return value + + +async def apply_ui_filters(page: Page, args: argparse.Namespace) -> None: + await expand_more_filters(page, args) + if args.dependencia: + clicked = await click_filter_option(page, args, args.dependencia) + if not clicked: + logger.warning( + "No se encontró la dependencia %s en el filtro UI.", args.dependencia + ) + await expand_more_filters(page, args) + for category in getattr(args, "category_path", []): + await expand_more_filters(page, args) + clicked = await click_filter_option(page, args, category) + if not clicked: + logger.warning("No se encontró la categoría %s en el filtro UI.", category) + await expand_more_filters(page, args) + if args.desde: + desde_input = page.locator("input[placeholder='Desde']") + if await desde_input.count(): + await desde_input.fill(normalize_ui_date(args.desde)) + if args.hasta: + hasta_input = page.locator("input[placeholder='Hasta']") + if await hasta_input.count(): + await hasta_input.fill(normalize_ui_date(args.hasta)) + apply_button = page.locator("button:has-text('Aplicar')") + if not await apply_button.count(): + apply_button = page.locator("button:has-text('Buscar')") + if await apply_button.count(): + ui_debug(args, "Aplicando filtros (botón 'Aplicar'/'Buscar')") + await apply_button.first.click() + else: + await page.keyboard.press("Enter") + await page.wait_for_timeout(1500) + + +def extract_year(value: Optional[str]) -> str: + if not value: + return "sin-anio" + match = re.search(r"(19|20)\d{2}", value) + return match.group(0) if match else "sin-anio" + + +def determine_document_path( + args: argparse.Namespace, record: Dict[str, Any], document: Dict[str, str] +) -> Path: + dependency = ( + args.dependencia + or document.get("dependency") + or record_field(record, ("dependencia", "origen", "tribunal")) + or "sin-dependencia" + ) + category = record_field( + record, ("rubro", "categoria", "tipo", "clasificacion", "subcategoria") + ) + year = extract_year(document.get("date")) + segments = [args.output_dir, safe_filename(dependency)] + if getattr(args, "category_path", None): + segments.extend( + safe_filename(part) for part in args.category_path if part and part.strip() + ) + elif category: + segments.append(safe_filename(category)) + segments.append(year) + return Path(*segments) / document["file_name"] + + +def build_document( + record: Dict[str, Any], pdf_template: str +) -> Optional[Dict[str, str]]: + attachment_id = extract_attachment_id(record) + if not attachment_id: + return None + pdf_url = derive_pdf_url(pdf_template, attachment_id) + document_date = record_field(record, ("fecha", "fechaDocumento", "fechaResolucion")) + title = record_field(record, ("titulo", "descripcion", "tituloDocumento", "nombre")) + dependency = record_field( + record, ("dependencia", "dependenciaTexto", "origen", "tribunal") + ) + parts = [ + document_date or "fecha-desconocida", + title or "sin-titulo", + dependency or "dependencia", + attachment_id, + ] + name = safe_filename("_".join(part for part in parts if part)) + return { + "attachment_id": attachment_id, + "pdf_url": pdf_url, + "file_name": f"{name}.pdf", + "title": title or "", + "date": document_date or "", + "dependency": dependency or "", + } + + +async def download_document( + context: APIRequestContext, destination: Path, document: Dict[str, str] +) -> None: + destination.parent.mkdir(parents=True, exist_ok=True) + if destination.exists(): + logger.info("Salteando %s, ya existe", destination.name) + return + response = await context.get( + document["pdf_url"], headers={"Referer": "https://www.pjn.gov.ar/"} + ) + response_ok = response.ok + if not response_ok: + logger.warning( + "Error al obtener %s (%s)", + document["attachment_id"], + response.status, + ) + return + content = await response.body() + destination.write_bytes(content) + logger.info("Descargado %s (%s)", destination.name, len(content)) + + +async def run_scraping( + args: argparse.Namespace, + signature: SearchSignature, + context_request: APIRequestContext, +) -> None: + documents_downloaded = 0 + page_start = ( + args.page_start + if args.page_start is not None + else next(iter(signature.page_keys.values()), 0) + ) + page_size = ( + args.page_size + if args.page_size is not None + else next(iter(signature.size_keys.values()), args.per_page) + ) + for page_index in range(args.max_pages): + if args.max_documents and documents_downloaded >= args.max_documents: + logger.info("Se alcanzó el límite de documentos solicitado.") + break + page_value = page_start + page_index + query_params, body_payload = prepare_page_payload( + signature, page_value, page_size + ) + apply_filter_overrides(query_params, body_payload, args.filter_overrides) + if signature.method == "GET": + response = await context_request.get( + signature.base_url, + params=query_params, + headers=signature.headers, + ) + else: + if body_payload is not None: + if signature.body_type == "form": + response = await context_request.post( + signature.base_url, + params=query_params, + data=body_payload, + headers=signature.headers, + ) + else: + response = await context_request.post( + signature.base_url, + params=query_params, + json=body_payload, + headers=signature.headers, + ) + else: + response = await context_request.post( + signature.base_url, + params=query_params, + headers=signature.headers, + ) + if not response.ok: + logger.warning("Respuesta %s en página %s", response.status, page_value) + break + payload = await response.json() + records = extract_records_by_path(payload, signature.listing_path) + if not records: + logger.info("No hay registros en la página %s", page_value) + break + logger.info("Procesando página %s (%d registros)", page_value, len(records)) + for record in records: + if args.max_documents and documents_downloaded >= args.max_documents: + break + doc = build_document(record, args.pdf_template) + if not doc: + continue + destination = determine_document_path(args, record, doc) + await download_document(context_request, destination, doc) + documents_downloaded += 1 + await asyncio.sleep(args.delay) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Scraper de gestión documental usando Playwright" + ) + parser.add_argument( + "--output-dir", + type=Path, + default=Path("descargas"), + help="Carpeta donde se guardan los PDFs", + ) + parser.add_argument( + "--pdf-template", + default="https://pjn-documento-api.pjn.gov.ar/api/documento/adjunto/{id}", + ) + parser.add_argument("--max-pages", type=int, default=20) + parser.add_argument("--per-page", type=int, default=25) + parser.add_argument( + "-f", + "--filter", + action="append", + default=[], + help="Filtros adicionales como clave=valor (aplica a query y/o cuerpo).", + ) + parser.add_argument( + "--dependencia", + help="Filtra por dependencia (ej. 'Fueros Federales' o 'Consejo de la Magistratura').", + ) + parser.add_argument( + "--desde", + help="Fecha mínima de publicación en formato ISO o compatible con el backend.", + ) + parser.add_argument( + "--hasta", + help="Fecha máxima de publicación en formato ISO o compatible con el backend.", + ) + parser.add_argument( + "--category", + action="append", + default=[], + help="Secuencia de categorías/subcategorías a seleccionar tras la dependencia.", + ) + parser.add_argument( + "--simulate-ui", + action="store_true", + help="Simula clicks/fechas en la UI antes de detectar la llamada REST.", + ) + parser.add_argument( + "--debug-ui", + action="store_true", + help="Imprime en consola cada intento de interacción con los filtros.", + ) + parser.add_argument( + "--delay", + type=float, + default=1.5, + help="Retraso entre páginas para respetar al servidor", + ) + parser.add_argument( + "--max-documents", + type=int, + default=0, + help="Máximo de PDFs a guardar (0 = infinito)", + ) + parser.add_argument( + "--headless", + action="store_true", + help="Ejecutar Playwright en modo headless", + ) + parser.add_argument( + "--signature-file", + type=Path, + help="Ruta a un JSON con la firma del API", + ) + parser.add_argument( + "--page-start", + type=int, + help="Valor base para el primer número de página", + ) + parser.add_argument( + "--page-size", type=int, help="Cantidad de registros por página" + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + if args.max_documents < 0: + raise ValueError("max-documents debe ser positivo o cero.") + args.output_dir.mkdir(parents=True, exist_ok=True) + args.category_path = args.category or [] + args.filter_overrides = build_filter_overrides( + args.filter, args.dependencia, args.desde, args.hasta + ) + + async def run(): + async with async_playwright() as p: + browser = await p.chromium.launch(headless=args.headless) + context = await browser.new_context(viewport={"width": 1400, "height": 900}) + page = await context.new_page() + signature: SearchSignature + if args.signature_file: + signature_data = load_signature_from_file(args.signature_file) + signature = load_signature(signature_data) + await page.goto( + "https://www.pjn.gov.ar/gestion-documental", + wait_until="networkidle", + ) + else: + signature = await capture_signature(page, args) + await run_scraping(args, signature, context.request) + await browser.close() + + asyncio.run(run()) + + +if __name__ == "__main__": + main() diff --git a/uv.lock b/uv.lock index f22747ea..594c7511 100644 --- a/uv.lock +++ b/uv.lock @@ -284,7 +284,7 @@ wheels = [ [[package]] name = "aymurai" -version = "1.1.13.dev29" +version = "1.1.13.dev32" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -337,6 +337,7 @@ dev = [ { name = "matplotlib" }, { name = "nbstripout" }, { name = "pip" }, + { name = "playwright" }, { name = "plotly" }, { name = "pre-commit" }, { name = "rich" }, @@ -396,6 +397,7 @@ dev = [ { name = "matplotlib", specifier = ">=3.10.0" }, { name = "nbstripout", specifier = ">=0.8.0" }, { name = "pip", specifier = ">=24.3.1" }, + { name = "playwright", specifier = "==1.56.0" }, { name = "plotly", specifier = ">=5.24.1" }, { name = "pre-commit", specifier = ">=4.0.1" }, { name = "rich", specifier = ">=13.9.4" }, @@ -2784,6 +2786,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731 }, ] +[[package]] +name = "playwright" +version = "1.56.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet" }, + { name = "pyee" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424 }, + { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228 }, + { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424 }, + { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122 }, + { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645 }, + { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837 }, + { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843 }, + { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959 }, +] + [[package]] name = "plotly" version = "6.5.0" @@ -3080,6 +3101,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, ] +[[package]] +name = "pyee" +version = "13.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730 }, +] + [[package]] name = "pygments" version = "2.19.2" From 0dbc86d93eedf4303b23e511e14e390dd52f7360 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Wed, 10 Dec 2025 21:56:19 +0000 Subject: [PATCH 024/101] =?UTF-8?q?=F0=9F=93=9D=20Add=20Jupyter=20notebook?= =?UTF-8?q?=20for=20entity=20disambiguation=20from=20pre-clustered=20valid?= =?UTF-8?q?ations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ation-from-pre-clustered-validations.ipynb | 899 ++++++++++++++++++ 1 file changed, 899 insertions(+) create mode 100644 notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb diff --git a/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb b/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb new file mode 100644 index 00000000..e0f49b96 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb @@ -0,0 +1,899 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c8fc128c", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8eeefe8b", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import mimetypes\n", + "import os\n", + "import re\n", + "import sqlite3\n", + "import time\n", + "import unicodedata\n", + "from collections import Counter\n", + "from operator import itemgetter\n", + "from pathlib import Path\n", + "from typing import Iterable\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import requests\n", + "from more_itertools import flatten, unique_everseen\n", + "from rapidfuzz import fuzz, process\n", + "\n", + "from aymurai.llm_providers import OllamaLLMProvider\n", + "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", + "from aymurai.utils.json_data import get_pretty, load_json, save_json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e0ec450", + "metadata": {}, + "outputs": [], + "source": [ + "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8999\")\n", + "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", + "DATA_ROOT = Path(\n", + " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/disambiguation-eval\")\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b017330", + "metadata": {}, + "outputs": [], + "source": [ + "def call_extraction_api(\n", + " session: requests.Session, file_path: Path\n", + ") -> dict[str, object]:\n", + " \"\"\"\n", + " Calls the document extraction API with the given file.\n", + "\n", + " Args:\n", + " session (requests.Session): The HTTP session to use for the request.\n", + " file_path (Path): The path to the file to be extracted.\n", + " Returns:\n", + " dict[str, object]: The response payload containing status and details.\n", + " \"\"\"\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " ENDPOINT,\n", + " files=files,\n", + " timeout=REQUEST_TIMEOUT_S,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef352767", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "print(f\"Discovered {len(documents)} documents.\")\n", + "documents" + ] + }, + { + "cell_type": "markdown", + "id": "5c1e471d", + "metadata": {}, + "source": [ + "## Fetch data from database" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "281524f4", + "metadata": {}, + "outputs": [], + "source": [ + "# Anonymization document\n", + "conn = sqlite3.connect(\"/workspace/sqlite/database.db\")\n", + "anonymization_document = pd.read_sql_query(\"SELECT * FROM anonymization_document\", conn)\n", + "conn.close()\n", + "\n", + "anonymization_document" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6db4fc5", + "metadata": {}, + "outputs": [], + "source": [ + "# Anonymization document paragraphs\n", + "conn = sqlite3.connect(\"/workspace/sqlite/database.db\")\n", + "anonymization_document_paragraph = pd.read_sql_query(\n", + " \"SELECT * FROM anonymization_document_paragraph\", conn\n", + ")\n", + "conn.close()\n", + "\n", + "anonymization_document_paragraph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee8bf74e", + "metadata": {}, + "outputs": [], + "source": [ + "# Anonymization paragraph\n", + "conn = sqlite3.connect(\"/workspace/sqlite/database.db\")\n", + "anonymization_paragraph = pd.read_sql_query(\n", + " \"SELECT * FROM anonymization_paragraph\", conn\n", + ")\n", + "conn.close()\n", + "\n", + "anonymization_paragraph" + ] + }, + { + "cell_type": "markdown", + "id": "ffd8f27e", + "metadata": {}, + "source": [ + "## Get particular document" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25086b1a", + "metadata": {}, + "outputs": [], + "source": [ + "doc_filename = \"\" # Replace with actual document filename\n", + "\n", + "# Get document ID\n", + "document_id = anonymization_document.loc[\n", + " anonymization_document[\"name\"].str.startswith(doc_filename), \"id\"\n", + "].values[0]\n", + "document_id" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "caa4628c", + "metadata": {}, + "outputs": [], + "source": [ + "# Get paragraph IDs for the document\n", + "paragraph_ids = anonymization_document_paragraph.loc[\n", + " anonymization_document_paragraph[\"document_id\"] == document_id, \"paragraph_id\"\n", + "].tolist()\n", + "\n", + "paragraph_ids" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18307aab", + "metadata": {}, + "outputs": [], + "source": [ + "# Get paragraphs\n", + "doc_paragraphs = anonymization_paragraph.loc[\n", + " anonymization_paragraph[\"id\"].isin(paragraph_ids)\n", + "]\n", + "doc_paragraphs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c7b4ffe", + "metadata": {}, + "outputs": [], + "source": [ + "# Get validations\n", + "validations = doc_paragraphs.loc[:, \"validation\"].map(eval).tolist()\n", + "validations = list(flatten(validations))\n", + "validations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a262978", + "metadata": {}, + "outputs": [], + "source": [ + "# Parse validations to extract labels and alt texts\n", + "validations = [\n", + " {\n", + " \"aymurai_label\": validation[\"attrs\"][\"aymurai_label\"],\n", + " \"text\": validation[\"attrs\"].get(\"aymurai_alt_text\") or validation[\"text\"],\n", + " }\n", + " for validation in validations\n", + "]\n", + "\n", + "validations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1de8c68f", + "metadata": {}, + "outputs": [], + "source": [ + "# Remove duplicates and sort\n", + "validations = list(unique_everseen(validations))\n", + "validations = sorted(\n", + " validations,\n", + " key=itemgetter(\"aymurai_label\", \"text\"),\n", + ")\n", + "validations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fb25d74", + "metadata": {}, + "outputs": [], + "source": [ + "# Fix 'TEL' labels\n", + "for validation in validations:\n", + " if validation[\"aymurai_label\"] == \"TEL\":\n", + " validation[\"aymurai_label\"] = \"TELEFONO\"\n", + "\n", + "validations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea1d64c9", + "metadata": {}, + "outputs": [], + "source": [ + "# Map 'TEXTO_ANONIMIZAR' to 'NOMBRE_ARCHIVO' when applicable\n", + "for validation in validations:\n", + " extension = os.path.splitext(validation[\"text\"])[1].lower()\n", + " if validation[\"aymurai_label\"] == \"TEXTO_ANONIMIZAR\":\n", + " if extension.endswith(\n", + " (\n", + " \"jpg\",\n", + " \"jpeg\",\n", + " \"png\",\n", + " \"pdf\",\n", + " \"docx\",\n", + " \"odt\",\n", + " \"mp4\",\n", + " \"enc\",\n", + " )\n", + " ):\n", + " validation[\"aymurai_label\"] = \"NOMBRE_ARCHIVO\"\n", + "\n", + "validations" + ] + }, + { + "cell_type": "markdown", + "id": "68a55889", + "metadata": {}, + "source": [ + "## Cluster entities based on textual" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7fa3f5b", + "metadata": {}, + "outputs": [], + "source": [ + "def normalize(s: str) -> str:\n", + " \"\"\"\n", + " Normalize string for clustering.\n", + "\n", + " Args:\n", + " s (str): input string\n", + "\n", + " Returns:\n", + " str: normalized string\n", + " \"\"\"\n", + " # strip accents, lowercase, collapse spaces\n", + " s = \"\".join(\n", + " c for c in unicodedata.normalize(\"NFD\", s) if unicodedata.category(c) != \"Mn\"\n", + " )\n", + " s = \" \".join(s.lower().split())\n", + " return s\n", + "\n", + "\n", + "def cluster_with_cdist(\n", + " entities: list[str], threshold: int = 90, scorer: callable = fuzz.token_set_ratio\n", + "):\n", + " \"\"\"\n", + " Cluster entities based on similarity using a distance matrix.\n", + "\n", + " Args:\n", + " entities (list[str]): list of entity strings\n", + " threshold (int): similarity threshold for clustering\n", + " scorer (callable): similarity scoring function\n", + "\n", + " Returns:\n", + " list[list[tuple[str, str]]]: clusters of (original, normalized) entity tuples\n", + " \"\"\"\n", + " # matrix of similarities on normalized strings\n", + " # normalize however you like; here just lower + strip\n", + " normed = [\" \".join(e.lower().split()) for e in entities]\n", + " sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold)\n", + " sim = np.array(sim)\n", + "\n", + " # union-find\n", + " parent = list(range(len(normed)))\n", + "\n", + " def find(i):\n", + " while parent[i] != i:\n", + " parent[i] = parent[parent[i]]\n", + " i = parent[i]\n", + " return i\n", + "\n", + " def union(i, j):\n", + " ri, rj = find(i), find(j)\n", + " if ri != rj:\n", + " parent[rj] = ri\n", + "\n", + " # link pairs above threshold (upper triangle only)\n", + " n = len(normed)\n", + " for i in range(n):\n", + " for j in range(i + 1, n):\n", + " if sim[i, j] >= threshold:\n", + " union(i, j)\n", + "\n", + " # collect clusters\n", + " clusters = {}\n", + " for idx in range(n):\n", + " root = find(idx)\n", + " clusters.setdefault(root, []).append((entities[idx], normed[idx]))\n", + " return list(clusters.values())\n", + "\n", + "\n", + "def pick_canonical(cluster: list[tuple[str, str]]) -> str:\n", + " \"\"\"\n", + " Pick canonical text from a cluster.\n", + "\n", + " Args:\n", + " cluster (list[tuple[str, str]]): cluster of (original, normalized) entity tuples\n", + "\n", + " Returns:\n", + " str: chosen canonical text\n", + " \"\"\"\n", + " # prefer longest original; fall back to first\n", + " return max(cluster, key=lambda x: len(x[0]))[0]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d57afd", + "metadata": {}, + "outputs": [], + "source": [ + "# Cluster entities\n", + "clusters = cluster_with_cdist(\n", + " [f\"{v['aymurai_label']}:{v['text']}\" for v in validations],\n", + " threshold=95,\n", + ")\n", + "\n", + "for cluster in clusters:\n", + " canonical = pick_canonical(cluster)\n", + " originals = [c[0] for c in cluster]\n", + " print(f\"Canonical: {canonical} -> {originals}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d542a89", + "metadata": {}, + "outputs": [], + "source": [ + "def parse_item(item: tuple[str, ...]) -> tuple[str, str, str]:\n", + " \"\"\"\n", + " Parse an item into (label, orig, norm).\n", + "\n", + " Accepts:\n", + " - (orig, norm, label)\n", + " - (labelled_orig, labelled_norm) with prefix 'LABEL:'\n", + " Args:\n", + " item (tuple[str, ...]): input item\n", + "\n", + " Returns:\n", + " tuple[str, str, str]: (label, orig, norm)\n", + " \"\"\"\n", + " if len(item) == 3:\n", + " orig, norm, label = item\n", + " return label, orig, norm\n", + "\n", + " # len == 2: assume \"LABEL:text\"\n", + " labelled_orig, labelled_norm = item\n", + " label, orig = labelled_orig.split(\":\", 1)\n", + " _, norm = labelled_norm.split(\":\", 1)\n", + " return label, orig, norm\n", + "\n", + "\n", + "def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Pick the most common label from parsed items.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen label\n", + " \"\"\"\n", + " labels = [lbl for lbl, _, _ in parsed_items]\n", + " # majority vote; fallback to first\n", + " return Counter(labels).most_common(1)[0][0]\n", + "\n", + "\n", + "def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Choose the longest original surface form; tweak as needed.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen canonical text\n", + " \"\"\"\n", + " return max(parsed_items, key=lambda x: len(x[1]))[1]\n", + "\n", + "\n", + "def clusters_to_canonical_entities(\n", + " clusters: list[list[tuple[str, str]]],\n", + ") -> list[CanonicalEntity]:\n", + " \"\"\"\n", + " Convert clusters to CanonicalEntity objects.\n", + "\n", + " Args:\n", + " clusters (list[list[tuple[str, str]]]): clusters of (original, normalized) entity tuples\n", + "\n", + " Returns:\n", + " list[CanonicalEntity]: list of CanonicalEntity objects\n", + " \"\"\"\n", + " canonical_entities = []\n", + "\n", + " for cluster in clusters:\n", + " parsed = [parse_item(item) for item in cluster] # [(label, orig, norm), ...]\n", + " label = pick_cluster_label(parsed)\n", + " canonical_text = pick_canonical_text(parsed)\n", + " aliases = sorted({orig for _, orig, _ in parsed})\n", + " ce = CanonicalEntity(\n", + " aymurai_label=label,\n", + " canonical_text=canonical_text,\n", + " aliases=aliases,\n", + " attributes={},\n", + " relations=[],\n", + " )\n", + " canonical_entities.append(ce)\n", + "\n", + " return canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "646a5e41", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = clusters_to_canonical_entities(clusters)\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "edbf9486", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort by label and canonical text\n", + "canonical_entities = sorted(\n", + " canonical_entities,\n", + " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", + ")\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e063eb07", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare target filename\n", + "target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_filename))[0]\n", + ")\n", + "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)\n", + "\n", + "# Save canonical entities to JSON for reviewing\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"/resources/data/restricted/disambiguation-eval/canonical-entities/REVIEWING/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81be5a59", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute this cell to load reviewed canonical entities\n", + "reviewed_canonical_entities = load_json(\n", + " \"/resources/data/restricted/disambiguation-eval/canonical-entities/REVIEWING/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", + "]\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "893a6e06", + "metadata": {}, + "outputs": [], + "source": [ + "# Persist reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"/resources/data/restricted/disambiguation-eval/canonical-entities/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "37eb822d", + "metadata": {}, + "source": [ + "## CanonicalEntity extraction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5eed876e", + "metadata": {}, + "outputs": [], + "source": [ + "# Available models\n", + "model = \"phi4:14b\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73400ead", + "metadata": {}, + "outputs": [], + "source": [ + "# Sanity check\n", + "provider = OllamaLLMProvider(model=model)\n", + "provider.generate(\"hola, ¿cómo estás?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b8ac734", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = \"\"\"\n", + "Eres un asistente especializado en anonimización de sentencias judiciales.\n", + "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", + "Una **entidad canónica** es la representación única de una entidad real.\n", + "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", + "\n", + "# Reglas\n", + "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", + "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", + "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", + "- Cada entidad canónica debe incluir:\n", + " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", + " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", + " - `aliases` (todas las menciones textuales relevantes).\n", + " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", + "\n", + "# Notas\n", + "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", + "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + "\n", + "# Ejemplo\n", + "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", + "A: ```json\n", + "[\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Laura Beatriz Gómez\",\n", + " \"aliases\": [\"Laura Beatriz Gómez\"],\n", + " \"attributes\": {\"role\": \"Denunciante\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"42987654\",\n", + " \"aliases\": [\"42.987.654\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DIRECCION\",\n", + " \"canonical_text\": \"calle Falsa 123\",\n", + " \"aliases\": [\"calle Falsa 123\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", + " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", + " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", + " \"attributes\": {\"role\": \"Denunciado\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"27654321\",\n", + " \"aliases\": [\"27.654.321\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"calle Real 789\",\n", + " \"aliases\": [\"calle Real 789\"]\n", + " }\n", + "]\n", + "```\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50d9cd2d", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template = \"\"\"\n", + "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", + "\n", + "# Documento\n", + "{document_text}\n", + "\n", + "# Menciones de entidades canónicas agrupadas\n", + "{canonical_entities}\n", + "\n", + "# Instrucciones\n", + "1. Evalúa cada entidad canónica listada, poniendo especial atención en las aliases.\n", + "2. Si alguna alias no corresponde a esa entidad, elimínala de la lista de aliases y créala como una nueva entidad canónica.\n", + "3. Las diferencias textuales correctas en los aliases pueden ser variantes ortográficas, abreviaciones, iniciales o errores tipográficos.\n", + "4. Diferencias sutiles que permiten desambiguar entidades distintas (por ejemplo, nombres similares de personas diferentes, fechas próximas pero distintas, números de documentos, códigos de identificación o nombres de archivos con pequeñas variaciones, etc.).\n", + "5. Normaliza canonical_text; mantén los aliases tal como aparecen.\n", + "6. Identifica roles u otros atributos relevantes en el campo attributes (por ejemplo, {{\"role\": \"Denunciante\"}}).\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81f47270", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "session = requests.Session()\n", + "doc_path = next((doc for doc in documents if doc.name == doc_filename), None)\n", + "document = call_extraction_api(session, Path(doc_path))\n", + "document = document.get(\"detail\", {}).get(\"document\")\n", + "\n", + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6143dc5", + "metadata": {}, + "outputs": [], + "source": [ + "# Remove empty 'entity_id', 'relations' and 'attributes' fields\n", + "canonical_entities = [\n", + " {\n", + " k: v\n", + " for k, v in ce.items()\n", + " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", + " }\n", + " for ce in canonical_entities\n", + "]\n", + "\n", + "# Prepare user prompt\n", + "user_prompt = user_prompt_template.format(\n", + " document_text=\"\\n\".join(document).strip(),\n", + " canonical_entities=get_pretty(canonical_entities),\n", + ")\n", + "\n", + "print(user_prompt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd772da7", + "metadata": {}, + "outputs": [], + "source": [ + "# Get canonical entities from the model\n", + "provider = OllamaLLMProvider(model=model)\n", + "response = provider.generate(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options={\"temperature\": 0, \"num_ctx\": 16_384},\n", + " format=CanonicalEntities.model_json_schema(),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "adb137aa", + "metadata": {}, + "outputs": [], + "source": [ + "response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "687c02b5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 26033a8f0566fa0fcfad7b2a7f814ec0e7a1679a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Wed, 17 Dec 2025 17:15:15 -0300 Subject: [PATCH 025/101] Feature/pdf extraction upgrade (#65) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 Configure VSCode Python env and Copilot scopes * 🔧 Include resources/llm in .dockerignore * 📌 Update dependencies in pyproject.toml and uv.lock * 🔧 Update Dockerfile and devcontainer.json to install additional PDF tooling * ♻️ Refactor Makefile and docker-compose.yml for improved service configuration and flexibility * 🚧 FIXME: Remove DecisionConv1dBinRegex model from pipeline configuration for dependencies update compatibility * 🔧 Set weights_only=False for torch.load compatibility * ✨ Enhance PDF extraction with marker integration and improved text processing * 🔧 Update run_safe_text_extraction to allow indefinite timeout by default * ✨ Add warm_marker_models function to initialize marker-pdf artifacts at startup * 🔥 Remove unused environment variables and rename TRANSFORMERS_CACHE to HF_HOME * 🔧 Improve service stopping logic for Ollama and API services in Makefile * 🔖 Bump aymurai package version to 2.0.0-alpha.1 * 🔧 Update HF_HOME path and remove HF_DATASETS_CACHE variable in .env.common * 🔧 Update OLLAMA_HOST for GPU-enabled services to point to ollama-gpu * 🔧 Simplify marker model warming logic by removing error handling * ♻️ Refactor text extraction into modular format-specific extractors * ✅ Add unit tests for document extraction and error handling * ➕ Add marker-pdf stack and drop textract * 🔧 Enhance PDF extraction with caching mechanism * 📝 Improve cache utility functions with enhanced docstrings and type hints * 🔧 Enhance cache key generation in PdfExtractor for improved stability and performance * 🔖 Update aymurai package version to 2.0.0a2.dev9 --- .dockerignore | 1 + .env.common | 22 +- .vscode/settings.json | 23 + Makefile | 96 ++- .../routers/misc/document_extract.py | 5 +- aymurai/api/main.py | 10 +- aymurai/api/startup/marker.py | 13 + aymurai/models/decision/tokenizer.py | 2 +- aymurai/text/extraction.py | 292 +------ aymurai/text/extractors/__init__.py | 19 + aymurai/text/extractors/base.py | 112 +++ aymurai/text/extractors/docx.py | 30 + aymurai/text/extractors/odt.py | 26 + aymurai/text/extractors/pdf.py | 74 ++ aymurai/text/extractors/utils.py | 298 +++++++ aymurai/utils/cache.py | 66 +- docker-compose.yml | 111 ++- docker/api/Dockerfile | 3 +- pyproject.toml | 8 +- .../production/full-paragraph/pipeline.json | 10 - test/text/test_extraction.py | 94 +++ test/text/test_extractors.py | 109 +++ uv.lock | 737 +++++++++++------- 23 files changed, 1496 insertions(+), 665 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 aymurai/api/startup/marker.py create mode 100644 aymurai/text/extractors/__init__.py create mode 100644 aymurai/text/extractors/base.py create mode 100644 aymurai/text/extractors/docx.py create mode 100644 aymurai/text/extractors/odt.py create mode 100644 aymurai/text/extractors/pdf.py create mode 100644 aymurai/text/extractors/utils.py create mode 100644 test/text/test_extraction.py create mode 100644 test/text/test_extractors.py diff --git a/.dockerignore b/.dockerignore index 9b91c7d3..e84e738a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ resources/ !resources/api !resources/pipelines +!resources/llm !resources/cache/aymurai !resources/cache/tfhub_modules notebooks/ diff --git a/.env.common b/.env.common index 5b5d5640..5760c797 100644 --- a/.env.common +++ b/.env.common @@ -1,11 +1,7 @@ -DATASETS_BASEPATH=/resources/datasets -MODELS_BASEPATH=/resources/models - +CACHE_PATH=/resources/cache AYMURAI_CACHE_BASEPATH=/resources/cache/aymurai -CACHE_PATH=/resources/cache -HF_DATASETS_CACHE=/resources/cache/huggingface/cache -TRANSFORMERS_CACHE=/resources/cache/huggingface/transformers +HF_HOME=/resources/cache/huggingface TOKENIZERS_PARALLELISM=1 TESSDATA_PREFIX=/usr/local/share/tessdata @@ -15,17 +11,3 @@ AYMURAI_RESTRICTED_DOCUMENT_DOCS_PATH="/resources/data/restricted/ar-juz-pcyf-10 TF_CPP_MIN_LOG_LEVEL=3 TFHUB_CACHE_DIR=/resources/cache/tfhub_modules - -TORCH_VERSION=2.0.1 -CUDA_VERSION=cu118 - -CORE_IMAGE_CUDA=registry.gitlab.com/collective.ai/datagenero-public/aymurai-core -CORE_IMAGE_CPU=registry.gitlab.com/collective.ai/datagenero-public/aymurai-core-cpu -API_IMAGE=registry.gitlab.com/collective.ai/datagenero-public/aymurai-api - -API_HOST=0.0.0.0 -API_PORT=8899 - -SRC_VOLUME_MOUNT=src:/src -RESOURCES_VOLUME_MOUNT=resources:/resources -NOTEBOOKS_VOLUME_MOUNT=notebooks:/notebooks diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..3fa21ad3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:system", + "python-envs.pythonProjects": [], + "files.associations": { + ".csv": "csv", + ".env*": "dotenv", + ".json": "json", + ".jsonc": "jsonc", + ".jsonl": "jsonl", + ".md": "markdown" + }, + "github.copilot.enable": { + "*": true, + "dotenv": false, + "csv": false, + "json": false, + "jsonc": false, + "jsonl": false, + "markdown": false, + "plaintext": false, + "scminput": false + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index 14e1d527..d828a9e1 100644 --- a/Makefile +++ b/Makefile @@ -2,70 +2,118 @@ include .env export $(shell sed 's/=.*//' .env) include .env.common export $(shell sed 's/=.*//' .env.common) -OLLAMA_CONTAINER := $(shell docker ps -q -f name=^ollama$$) + +# Select which Ollama service to control (ollama or ollama-gpu) +OLLAMA_SERVICE ?= ollama +OLLAMA_CONTAINER := $(shell docker ps -q -f name=^$(OLLAMA_SERVICE)$$) + +# Select which API service to control (override with API_SERVICE=aymurai-api-gpu) +API_SERVICE ?= aymurai-api +# Select which full API service to control (override with API_FULL_SERVICE=aymurai-api-full-gpu) +API_FULL_SERVICE ?= aymurai-api-full api-build: - docker compose build aymurai-api + docker compose build $(API_SERVICE) api-run: - docker compose run --service-ports aymurai-api + @if [ "$(API_SERVICE)" = "aymurai-api-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose run --service-ports $(API_SERVICE) api-up: - docker compose up -d aymurai-api + @if [ "$(API_SERVICE)" = "aymurai-api-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose up -d $(API_SERVICE) api-stop: - docker compose stop aymurai-api + docker compose stop $(API_SERVICE) api-logs: - docker compose logs -f aymurai-api + docker compose logs -f $(API_SERVICE) api-pull: - docker compose pull aymurai-api + docker compose pull $(API_SERVICE) api-full-build: - docker compose build aymurai-api-full + docker compose build $(API_FULL_SERVICE) api-full-run: - docker compose run --service-ports aymurai-api-full + @if [ "$(API_FULL_SERVICE)" = "aymurai-api-full-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose run --service-ports $(API_FULL_SERVICE) api-full-up: - docker compose up -d aymurai-api-full + @if [ "$(API_FULL_SERVICE)" = "aymurai-api-full-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose up -d $(API_FULL_SERVICE) api-full-stop: - docker compose stop aymurai-api-full + docker compose stop $(API_FULL_SERVICE) api-full-logs: - docker compose logs -f aymurai-api-full + docker compose logs -f $(API_FULL_SERVICE) api-full-pull: - docker compose pull aymurai-api-full + docker compose pull $(API_FULL_SERVICE) ollama-up: - docker compose up -d --no-recreate ollama + @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose up -d --no-recreate $(OLLAMA_SERVICE) ollama-stop: - docker compose stop ollama + docker compose stop $(OLLAMA_SERVICE) ollama-restart: - docker compose restart ollama + docker compose restart $(OLLAMA_SERVICE) ollama-pull: ifndef MODEL $(error MODEL variable is required, e.g. make ollama-pull MODEL=llama3) endif ifndef OLLAMA_CONTAINER - $(error Ollama container 'ollama' is not running. Start it first with 'make ollama-up') + $(error Ollama container '$(OLLAMA_SERVICE)' is not running. Start it first with 'make ollama-up OLLAMA_SERVICE=$(OLLAMA_SERVICE)') endif - docker exec ollama ollama pull $(MODEL) + docker exec $(OLLAMA_SERVICE) ollama pull $(MODEL) ollama-run: ifndef MODEL $(error MODEL variable is required, e.g. make ollama-run MODEL=llama3) endif - docker compose up -d --no-recreate ollama - docker compose exec -it ollama ollama run $(MODEL) + @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose up -d --no-recreate $(OLLAMA_SERVICE) + docker compose exec -it $(OLLAMA_SERVICE) ollama run $(MODEL) ollama-list: - docker compose up -d --no-recreate ollama - docker compose exec ollama ollama list + @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose up -d --no-recreate $(OLLAMA_SERVICE) + docker compose exec $(OLLAMA_SERVICE) ollama list ollama-rm: ifndef MODEL $(error MODEL variable is required, e.g. make ollama-rm MODEL=llama3) endif - docker compose up -d --no-recreate ollama - docker compose exec ollama ollama rm $(MODEL) + @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ + docker compose stop ollama || true; \ + else \ + docker compose stop ollama-gpu || true; \ + fi + docker compose up -d --no-recreate $(OLLAMA_SERVICE) + docker compose exec $(OLLAMA_SERVICE) ollama rm $(MODEL) stress-test: locust -f locustfile.py --host http://localhost:8899 diff --git a/aymurai/api/endpoints/routers/misc/document_extract.py b/aymurai/api/endpoints/routers/misc/document_extract.py index be5d4d63..bdbcfc2e 100644 --- a/aymurai/api/endpoints/routers/misc/document_extract.py +++ b/aymurai/api/endpoints/routers/misc/document_extract.py @@ -28,14 +28,15 @@ def extraction(path: str) -> str: return document_normalize(text) if text else "" -def run_safe_text_extraction(path: str, timeout_s: float = 30) -> str: +def run_safe_text_extraction(path: str, timeout_s: float | None = None) -> str: """ Runs the text extraction in a separate process to avoid blocking the main thread. This is useful for long-running tasks or when the extraction might hang. Args: path (str): Path to the file to be processed. - timeout_s (float): Timeout in seconds for the extraction process. Defaults to 30 seconds. + timeout_s (float | None): Timeout in seconds for the extraction process. + If None, waits indefinitely. Defaults to None. Returns: str: Extracted text from the document. diff --git a/aymurai/api/main.py b/aymurai/api/main.py index d814a3f6..fbd5bdad 100644 --- a/aymurai/api/main.py +++ b/aymurai/api/main.py @@ -6,14 +6,15 @@ from alembic import command from alembic.config import Config from fastapi import FastAPI, Request -from fastapi.responses import RedirectResponse from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import RedirectResponse from aymurai.api import core +from aymurai.api.startup.database import check_db_connection +from aymurai.api.startup.marker import warm_marker_models from aymurai.logger import get_logger -from aymurai.settings import settings from aymurai.pipeline import AymurAIPipeline -from aymurai.api.startup.database import check_db_connection +from aymurai.settings import settings try: from aymurai.version import __version__ @@ -104,10 +105,11 @@ def healthcheck(): if __name__ == "__main__": # download the necessary data - logger.info("Loading pipelines and exit.") + logger.info("Loading pipelines") AymurAIPipeline.load( os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "flair-anonymizer") ) AymurAIPipeline.load( os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "full-paragraph") ) + warm_marker_models() diff --git a/aymurai/api/startup/marker.py b/aymurai/api/startup/marker.py new file mode 100644 index 00000000..208afa43 --- /dev/null +++ b/aymurai/api/startup/marker.py @@ -0,0 +1,13 @@ +from aymurai.logger import get_logger +from aymurai.text.extractors.utils import get_marker_pdf_converter_and_md_renderer + +logger = get_logger(__name__) + + +def warm_marker_models() -> None: + """Download marker-pdf artifacts at startup to avoid first-request latency.""" + try: + get_marker_pdf_converter_and_md_renderer() + logger.info("marker-pdf models are ready") + except Exception as exc: + logger.warning("marker-pdf warmup failed: %s", exc) diff --git a/aymurai/models/decision/tokenizer.py b/aymurai/models/decision/tokenizer.py index bbb21eae..bbd5cae4 100644 --- a/aymurai/models/decision/tokenizer.py +++ b/aymurai/models/decision/tokenizer.py @@ -15,7 +15,7 @@ def save(self, path: str): @classmethod def load(cls, path: str): - vocab = torch.load(path) + vocab = torch.load(path, weights_only=False) return cls(vocab=vocab) def __call__(self, text: str): diff --git a/aymurai/text/extraction.py b/aymurai/text/extraction.py index 66cc76b0..1e2735d2 100644 --- a/aymurai/text/extraction.py +++ b/aymurai/text/extraction.py @@ -1,31 +1,17 @@ import logging import mimetypes import os -import statistics -import unicodedata import zipfile from pathlib import Path -from typing import Any from zipfile import BadZipFile -import numpy as np -import pymupdf -import textract -import xmltodict -from lxml import etree -from more_itertools import flatten -from textract.exceptions import ShellError -from textract.parsers import _get_available_extensions - from aymurai.logger import get_logger -from aymurai.utils.misc import get_element, get_recursively +from aymurai.text.extractors import SUPPORTED_EXTENSIONS, InvalidFile, get_extractor logger = get_logger(__file__) -TEXTRACT_EXTENSIONS = _get_available_extensions() MIMETYPE_EXTENSION_MAPPER = { "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx", - "application/msword": "doc", "application/vnd.oasis.opendocument.text": "odt", "application/pdf": "pdf", } @@ -34,12 +20,6 @@ ERRORS = ["ignore", "coerce", "raise"] -class InvalidFile(Exception): - """Invalid File""" - - pass - - def _zip_contains(path: str, member: str) -> bool: """ Check if a zip file contains a specific member. @@ -100,130 +80,6 @@ def get_extension(path: str) -> str: return "unknown" -def _load_xml_from_odt(path: str, xmlfile: str = "styles.xml") -> str: - """ - Load xml file inside an odt. - - Args: - path (str): path to odt file. - xmlfile (str, optional): xml to open. Defaults to 'styles.xml'. - - Returns: - str: xml content. - """ - with zipfile.ZipFile(path, "r") as odt: - if xmlfile not in odt.namelist(): - return "" - with odt.open(xmlfile) as file: - content = file.read().decode("utf-8") - - return content - - -def _load_xml_from_docx(path: str, xmlfile: str = "word/footnotes.xml") -> Any | None: - """Extract XML content from a specific file inside a .docx.""" - with zipfile.ZipFile(path, "r") as docx: - if xmlfile not in docx.namelist(): - return - with docx.open(xmlfile) as f: - return etree.parse(f) - - -def get_header(path: str) -> list[str]: - """ - Extract header from styles.xml inside a ODT file. - - Args: - path (str): path to odt file. - - Returns: - list[str]: header lines. - """ - styles_xml_content = _load_xml_from_odt(path) - styles_dict = xmltodict.parse(styles_xml_content) - - header_root = get_element( - styles_dict, - levels=[ - "office:document-styles", - "office:master-styles", - "style:master-page", - ], - ) - - if not isinstance(header_root, list): - header_root = [header_root] - - style_header = [ - get_recursively(item, "style:header") - for item in header_root - if get_recursively(item, "style:header") - ] - style_header = list(flatten(style_header)) - - texts = [ - get_recursively(item, "#text") - for item in style_header - if get_recursively(item, "#text") - ] - texts = list(flatten(texts)) - - if not texts: - return [] - - return texts - - -def get_footnotes(path: str) -> list[str] | None: - """ - Extract footnotes from footnotes.xml inside a DOCX file. - - Args: - path (str): Path to the DOCX file. - - Returns: - list[str]: Footnote texts. - """ - footnotes_tree = _load_xml_from_docx(path) - if not footnotes_tree: - return - - footnotes_root = footnotes_tree.getroot() - - # Define the namespace map - ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"} - - # Extract footnote texts in order - footnotes_texts = [] - for footnote in footnotes_root.findall("w:footnote", namespaces=ns): - texts = footnote.xpath(".//w:t/text()", namespaces=ns) - if texts: - footnotes_texts.append("".join(texts)) - - return footnotes_texts - - -def pdf_to_text(filename: str, y_tolerance: float | None = None) -> str: - """ - Extract text from a PDF file. - - Args: - filename (str): Path to the PDF file. - y_tolerance (float, optional): - Maximum vertical gap (in points) to consider blocks part of the same paragraph. - - Returns: - str: Extracted text. - """ - if y_tolerance is None: - y_tolerance = compute_median_margin_between_blocks(filename) - - paragraphs = extract_and_merge_paragraphs(filename, np.ceil(y_tolerance)) - docu = "\n\n".join(paragraphs) - docu = unicodedata.normalize("NFKC", docu) - return docu - - def extract_document( filename: str | Path, errors: str = "ignore", @@ -249,135 +105,41 @@ def extract_document( Returns: str: extracted document text. """ - filename = str(filename) # patch for pathlib + filename = str(filename) if errors not in ERRORS: raise ValueError(f"errors argument must be in {ERRORS}") ext = get_extension(filename) - kwargs["extension"] = kwargs.get("extension", ext) - kwargs["output_encoding"] = kwargs.get("output_encoding", "utf-8") - - logger = get_logger(f"{__file__}.{__name__}") - - if errors == "ignore": - logger.setLevel(logging.ERROR) - - if ( - not isinstance(filename, str) - or not os.path.exists(filename) - or ext not in TEXTRACT_EXTENSIONS - ): + if (not isinstance(filename, str)) or not os.path.exists(filename): if errors == "raise": raise InvalidFile(f"Invalid path: {filename}") - logger.warn(f"skipping (invalid): {filename}") - return - - try: - if ext == "pdf": - return pdf_to_text(filename, y_tolerance=kwargs.get("y_tolerance")) + logger.warning("Skipping (missing): %s", filename) + return None - docu = textract.process(filename, **kwargs).decode("utf-8") - except (BadZipFile, KeyError, ShellError): + if ext not in SUPPORTED_EXTENSIONS: if errors == "raise": - raise - logger.warn(f"skipping (corrupted): {filename}") - return - - # patch header loading in odt files - if ext == "odt": - header = "\n".join(get_header(filename)) - docu = header + "\n\n" + docu - - # patch footnotes loading in docx files - if ext == "docx": - footnotes = get_footnotes(filename) or [] - footnotes = "\n".join(footnotes) - if footnotes.strip(): - docu = docu + "\n\n" + footnotes - - docu = unicodedata.normalize("NFKC", docu) - return docu - - -def compute_median_margin_between_blocks(pdf_path: str) -> float: - """ - Computes the median vertical margin between text blocks in a PDF. - - Args: - pdf_path (str): Path to the PDF file. - - Returns: - float: Median margin between text blocks (in points). - """ - margins = [] - - with pymupdf.open(pdf_path) as doc: - for page in doc: - # Extract all text blocks from the page - blocks = page.get_text("blocks") - - # Sort blocks by their top y-coordinate (y0) - blocks_sorted = sorted(blocks, key=lambda b: b[1]) - - # Compute vertical margins between consecutive blocks - for i in range(1, len(blocks_sorted)): - previous_block = blocks_sorted[i - 1] - current_block = blocks_sorted[i] - - # Calculate the vertical margin - previous_y1 = previous_block[3] # Bottom of the previous block - current_y0 = current_block[1] # Top of the current block - margin = current_y0 - previous_y1 - - if margin > 0: # Ignore overlapping blocks - margins.append(margin) - - # Compute and return the median margin - if margins: - return statistics.median(margins) - else: - return 0.0 # Return 0 if no margins were found + raise InvalidFile(f"Unsupported extension: {ext}") + logger.warning("Skipping (unsupported %s): %s", ext, filename) + return None + extractor = get_extractor(ext) -def extract_and_merge_paragraphs(pdf_path: str, y_tolerance=5) -> list[str]: - """ - Extracts and merges paragraphs from a PDF by grouping close text blocks. - - Args: - pdf_path (str): Path to the PDF file. - y_tolerance (float): Maximum vertical gap (in points) to consider blocks part of the same paragraph. - - Returns: - list[str]: A list of merged paragraphs as strings. - """ - paragraphs = [] - current_paragraph = [] - last_y1 = None - - with pymupdf.open(pdf_path) as doc: - for page in doc: - # Extract all text blocks from the page - blocks = page.get_text("blocks") - - # Sort blocks by their top y-coordinate (y0) - blocks_sorted = sorted(blocks, key=lambda b: b[1]) - - for block in blocks_sorted: - x0, y0, x1, y1, text, *_ = block - - if last_y1 is not None and (y0 - last_y1) > y_tolerance: - # If the gap between blocks is too large, start a new paragraph - if current_paragraph: - paragraphs.append(" ".join(current_paragraph)) - current_paragraph = [] - - current_paragraph.append(text) - last_y1 = y1 - - if current_paragraph: - paragraphs.append(" ".join(current_paragraph)) - current_paragraph = [] - - return paragraphs + try: + return extractor.extract(Path(filename)) + except InvalidFile as exc: + if errors == "raise": + raise + logger.warning("Skipping (corrupted): %s (%s)", filename, exc) + return None + except BadZipFile as exc: + if errors == "raise": + raise + logger.warning("Skipping (corrupted archive): %s (%s)", filename, exc) + return None + except Exception as exc: + if errors == "raise": + raise + logger.warning("Skipping (unexpected): %s (%s)", filename, exc) + return None diff --git a/aymurai/text/extractors/__init__.py b/aymurai/text/extractors/__init__.py new file mode 100644 index 00000000..01919448 --- /dev/null +++ b/aymurai/text/extractors/__init__.py @@ -0,0 +1,19 @@ +# Import concrete extractors so they self-register. +from aymurai.text.extractors import docx, odt, pdf # noqa: F401 +from aymurai.text.extractors.base import ( + BaseExtractor, + InvalidFile, + get_extractor, + register_extractor, + supported_extensions, +) + +SUPPORTED_EXTENSIONS = supported_extensions() + +__all__ = [ + "BaseExtractor", + "InvalidFile", + "SUPPORTED_EXTENSIONS", + "get_extractor", + "register_extractor", +] diff --git a/aymurai/text/extractors/base.py b/aymurai/text/extractors/base.py new file mode 100644 index 00000000..20282b09 --- /dev/null +++ b/aymurai/text/extractors/base.py @@ -0,0 +1,112 @@ +from abc import ABC, abstractmethod +from pathlib import Path + +from aymurai.logger import get_logger + +logger = get_logger(__file__) + + +class InvalidFile(Exception): + """Raised when an extractor receives an invalid or missing file.""" + + +class BaseExtractor(ABC): + """Common interface shared by all document extractors.""" + + # Lowercase file extension handled by the extractor, without dot. + extension: str + + def ensure_file(self, path: Path) -> Path: + """ + Ensure the input file exists before extraction. + + Args: + path (Path): Candidate file path. + + Raises: + InvalidFile: If the file does not exist. + + Returns: + Path: Validated path ready for extraction. + """ + if not path.exists(): + raise InvalidFile(f"Invalid path: {path}") + return path + + @abstractmethod + def extract(self, path: Path) -> str: + """ + Extract normalized text from the source document. + + Args: + path (Path): Input document path. + + Returns: + str: Cleaned textual content. + """ + + +_REGISTRY: dict[str, type[BaseExtractor]] = {} + + +def register_extractor(cls: type[BaseExtractor]) -> type[BaseExtractor]: + """ + Register an extractor class for a specific extension. + + Args: + cls (type[BaseExtractor]): Extractor class to register. + + Returns: + type[BaseExtractor]: Registered class for fluent decorator usage. + """ + extension = getattr(cls, "extension", None) + if not extension: + raise ValueError( + f"Extractor {cls.__name__} must define an 'extension' attribute" + ) + + normalized = extension.lower() + if normalized in _REGISTRY: + logger.warning( + "Overriding extractor for extension '%s' with %s", normalized, cls.__name__ + ) + + _REGISTRY[normalized] = cls + return cls + + +def get_extractor(extension: str) -> BaseExtractor: + """ + Retrieve an extractor instance for the desired extension. + + Args: + extension (str): File extension to resolve. + + Returns: + BaseExtractor: Ready-to-use extractor instance. + """ + normalized = extension.lower() + try: + extractor_cls = _REGISTRY[normalized] + except KeyError as exc: + raise ValueError(f"Unsupported extension: {extension}") from exc + return extractor_cls() + + +def supported_extensions() -> set[str]: + """ + List the registered document extensions. + + Returns: + set[str]: Known extensions handled by the registry. + """ + return set(_REGISTRY.keys()) + + +__all__ = [ + "BaseExtractor", + "InvalidFile", + "get_extractor", + "register_extractor", + "supported_extensions", +] diff --git a/aymurai/text/extractors/docx.py b/aymurai/text/extractors/docx.py new file mode 100644 index 00000000..ae698e20 --- /dev/null +++ b/aymurai/text/extractors/docx.py @@ -0,0 +1,30 @@ +from pathlib import Path +from zipfile import BadZipFile + +import docx2txt + +from aymurai.text.extractors.base import BaseExtractor, InvalidFile, register_extractor +from aymurai.text.extractors.utils import get_footnotes, normalize_text + + +@register_extractor +class DocxExtractor(BaseExtractor): + extension = "docx" + + def extract(self, path: Path) -> str: + file_path = self.ensure_file(path) + + try: + document_text = docx2txt.process(str(file_path)) or "" + except (OSError, BadZipFile, KeyError) as exc: + raise InvalidFile(str(exc)) from exc + except Exception as exc: + raise InvalidFile(str(exc)) from exc + + footnotes = get_footnotes(file_path) or [] + footnotes_text = "\n".join(note for note in footnotes if note.strip()) + + if footnotes_text: + document_text = f"{document_text}\n\n{footnotes_text}" + + return normalize_text(document_text) diff --git a/aymurai/text/extractors/odt.py b/aymurai/text/extractors/odt.py new file mode 100644 index 00000000..7aaf3caa --- /dev/null +++ b/aymurai/text/extractors/odt.py @@ -0,0 +1,26 @@ +from pathlib import Path +from zipfile import BadZipFile + +from xml.etree.ElementTree import ParseError + +from aymurai.text.extractors.base import BaseExtractor, InvalidFile, register_extractor +from aymurai.text.extractors.utils import get_header, normalize_text, odt_to_text + + +@register_extractor +class OdtExtractor(BaseExtractor): + extension = "odt" + + def extract(self, path: Path) -> str: + file_path = self.ensure_file(path) + + try: + document_text = odt_to_text(file_path) + except (OSError, ValueError, BadZipFile, KeyError, ParseError) as exc: + raise InvalidFile(str(exc)) from exc + + header = "\n".join(get_header(file_path)).strip() + if header: + document_text = f"{header}\n\n{document_text}" + + return normalize_text(document_text) diff --git a/aymurai/text/extractors/pdf.py b/aymurai/text/extractors/pdf.py new file mode 100644 index 00000000..7a7e41b3 --- /dev/null +++ b/aymurai/text/extractors/pdf.py @@ -0,0 +1,74 @@ +from hashlib import blake2b +from pathlib import Path + +from aymurai.logger import get_logger +from aymurai.text.extractors.base import BaseExtractor, InvalidFile, register_extractor +from aymurai.text.extractors.utils import pdf_to_text +from aymurai.utils.cache import cache_load, cache_save, get_cache_key + +logger = get_logger(__file__) + + +@register_extractor +class PdfExtractor(BaseExtractor): + extension = "pdf" + + def extract(self, path: Path) -> str: + file_path = self.ensure_file(path) + + # Check cache first + cache_key = self._cache_key(file_path) + if cache_key: + cached_text = cache_load(cache_key) + if cached_text is not None: + logger.debug("PDF cache hit for %s", file_path) + return cached_text + + try: + text = pdf_to_text(file_path) + except (OSError, ValueError) as exc: + raise InvalidFile(str(exc)) from exc + except Exception as exc: + raise InvalidFile(str(exc)) from exc + if cache_key: + cache_save(text, key=cache_key) + logger.debug("PDF cache stored for %s", file_path) + + return text + + @staticmethod + def _cache_key(file_path: Path) -> str | None: + """ + Compute a stable cache key for the PDF payload. + + Args: + file_path (Path): Location of the PDF file to fingerprint. + + Returns: + str | None: Deterministic cache key, or ``None`` when the file is unreadable. + """ + try: + stat = file_path.stat() + except OSError as exc: + logger.warning("Unable to stat PDF %s for caching: %s", file_path, exc) + return None + + try: + hasher = blake2b(digest_size=32) + with file_path.open("rb") as handle: + for chunk in iter(lambda: handle.read(65536), b""): + hasher.update(chunk) + fingerprint = hasher.hexdigest() + except OSError as exc: + logger.warning("Unable to hash PDF %s for caching: %s", file_path, exc) + fingerprint = None + + item = fingerprint or file_path.resolve().as_posix() + context = { + "component": "pdf-extractor", + "size": stat.st_size, + } + if fingerprint is None: + context["mtime_ns"] = stat.st_mtime_ns + + return get_cache_key(item, context=context) diff --git a/aymurai/text/extractors/utils.py b/aymurai/text/extractors/utils.py new file mode 100644 index 00000000..b7c9520b --- /dev/null +++ b/aymurai/text/extractors/utils.py @@ -0,0 +1,298 @@ +import os +import unicodedata +import xml.etree.ElementTree as ET +import zipfile +from functools import cache +from pathlib import Path +from typing import Any + +import markdown2 +import xmltodict +from bs4 import BeautifulSoup +from lxml import etree +from marker.converters.pdf import PdfConverter +from marker.models import create_model_dict +from marker.renderers.markdown import MarkdownRenderer +from more_itertools import flatten + +from aymurai.logger import get_logger +from aymurai.utils.misc import get_element, get_recursively + +logger = get_logger(__file__) + + +BLOCK_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "blockquote", "pre"} + +MARKER_PDF_CONFIG = { + "layout_batch_size": 8, + "detection_batch_size": 8, + "table_rec_batch_size": 8, + "recognition_batch_size": 8, + "ocr_error_batch_size": 8, + "force_ocr": True, + "strip_existing_ocr": True, +} + +ODT_NS = {"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0"} + + +def normalize_text(text: str) -> str: + """ + Normalize Unicode output consistently across extractors. + + Args: + text (str): Raw text extracted from a document. + + Returns: + str: Normalized string in NFKC form. + """ + return unicodedata.normalize("NFKC", text) + + +def markdown_to_text(md: str) -> str: + """ + Convert Markdown content to plain text by extracting relevant blocks. + + Args: + md (str): Markdown content produced by the renderer. + + Returns: + str: Plain text representation stripped of nested blocks. + """ + html = markdown2.markdown(md, extras=["tables"]) + soup = BeautifulSoup(html, "html.parser") + + chunks: list[str] = [] + for block in soup.find_all(BLOCK_TAGS): + if block.find_parent(BLOCK_TAGS): + continue + chunks.append(block.get_text(" ", strip=True)) + + return "\n\n".join(filter(None, chunks)) + + +def _build_marker_pdf_config() -> dict[str, int | str | bool]: + """ + Build marker configuration factoring in environment overrides. + + Returns: + dict[str, int | str | bool]: Effective configuration for marker-pdf. + """ + config = MARKER_PDF_CONFIG.copy() + + torch_device = os.getenv("TORCH_DEVICE") + if torch_device: + config["TORCH_DEVICE"] = torch_device + + log_level = os.getenv("LOG_LEVEL", "").lower() + if log_level == "debug": + config["debug"] = True + + return config + + +@cache +def get_marker_pdf_converter_and_md_renderer() -> tuple[PdfConverter, MarkdownRenderer]: + """ + Provide cached marker PDF converter and Markdown renderer instances. + + Returns: + tuple[PdfConverter, MarkdownRenderer]: Ready-to-use converter and renderer. + """ + pdf_converter = PdfConverter( + artifact_dict=create_model_dict(), + config=_build_marker_pdf_config(), + ) + + markdown_renderer = MarkdownRenderer( + { + "keep_pageheader_in_output": True, + "keep_pagefooter_in_output": True, + } + ) + + return pdf_converter, markdown_renderer + + +def pdf_to_text(file_path: Path) -> str: + """ + Extract text from a PDF file and return normalized plain text. + + Args: + file_path (Path): Path to the PDF document. + + Returns: + str: Cleaned textual content extracted from the PDF. + """ + logger.info("Extracting text from PDF: %s", file_path) + pdf_converter, markdown_renderer = get_marker_pdf_converter_and_md_renderer() + document = pdf_converter.build_document(filepath=file_path.as_posix()) + markdown_output = markdown_renderer(document) + plain_text = markdown_to_text(markdown_output.markdown) + return normalize_text(plain_text) + + +def load_xml_from_docx(path: Path, xmlfile: str = "word/footnotes.xml") -> Any | None: + """ + Extract XML content from a specific file inside a DOCX container. + + Args: + path (Path): DOCX archive path. + xmlfile (str, optional): Internal member name to inspect. Defaults to "word/footnotes.xml". + + Returns: + Any | None: Parsed XML tree or None when the member is missing. + """ + with zipfile.ZipFile(path, "r") as docx: + if xmlfile not in docx.namelist(): + return None + with docx.open(xmlfile) as handle: + return etree.parse(handle) + + +def load_xml_from_odt(path: Path, xmlfile: str = "styles.xml") -> str: + """ + Load XML content from an ODT archive member. + + Args: + path (Path): ODT archive path. + xmlfile (str, optional): Member name to open. Defaults to "styles.xml". + + Returns: + str: UTF-8 decoded XML content or an empty string when absent. + """ + with zipfile.ZipFile(path, "r") as odt: + if xmlfile not in odt.namelist(): + return "" + with odt.open(xmlfile) as file: + return file.read().decode("utf-8") + + +def get_header(path: Path) -> list[str]: + """ + Extract header text defined in an ODT stylesheet. + + Args: + path (Path): ODT document path. + + Returns: + list[str]: Header snippets collected from the stylesheet. + """ + styles_xml_content = load_xml_from_odt(path) + if not styles_xml_content: + return [] + + styles_dict = xmltodict.parse(styles_xml_content) + + header_root = get_element( + styles_dict, + levels=[ + "office:document-styles", + "office:master-styles", + "style:master-page", + ], + ) + + if not isinstance(header_root, list): + header_root = [header_root] + + style_header = [ + get_recursively(item, "style:header") + for item in header_root + if get_recursively(item, "style:header") + ] + style_header = list(flatten(style_header)) + + texts = [ + get_recursively(item, "#text") + for item in style_header + if get_recursively(item, "#text") + ] + texts = list(flatten(texts)) + + return [text for text in texts if isinstance(text, str)] + + +def get_footnotes(path: Path) -> list[str] | None: + """ + Extract footnotes from a DOCX document. + + Args: + path (Path): DOCX document path. + + Returns: + list[str] | None: Ordered footnote texts or None when absent. + """ + footnotes_tree = load_xml_from_docx(path) + if not footnotes_tree: + return None + + footnotes_root = footnotes_tree.getroot() + ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"} + + footnotes_texts: list[str] = [] + for footnote in footnotes_root.findall("w:footnote", namespaces=ns): + texts = footnote.xpath(".//w:t/text()", namespaces=ns) + if texts: + footnotes_texts.append("".join(texts)) + + return footnotes_texts + + +def _odt_qn(name: str) -> str: + """ + Resolve a qualified ODT tag into the ElementTree namespace form. + + Args: + name (str): Namespaced tag in the ``prefix:local`` format. + + Returns: + str: Expanded tag with namespace URI. + """ + prefix, local = name.split(":", 1) + return f"{{{ODT_NS[prefix]}}}{local}" + + +def _odt_text_to_string(elem) -> str: + """ + Flatten an ODT Element into a plain-text string, preserving special tokens. + + Args: + elem (Element): XML element to flatten. + + Returns: + str: Concatenated text content for the element subtree. + """ + out = elem.text or "" + for child in elem: + if child.tag == _odt_qn("text:tab"): + out += "\t" + elif child.tag == _odt_qn("text:s"): + out += " " * int(child.get(_odt_qn("text:c"), 1)) + else: + out += _odt_text_to_string(child) + if child.tail: + out += child.tail + return out + + +def odt_to_text(path: Path) -> str: + """ + Extract text content, preserving paragraphs and headings, from an ODT file. + + Args: + path (Path): ODT document path. + + Returns: + str: Plain text version of the document content. + """ + with zipfile.ZipFile(path, "r") as archive: + content_xml = archive.read("content.xml") + + content = ET.fromstring(content_xml) + + lines = [] + for child in content.iter(): + if child.tag in (_odt_qn("text:p"), _odt_qn("text:h")): + lines.append(_odt_text_to_string(child)) + return "\n".join(lines) diff --git a/aymurai/utils/cache.py b/aymurai/utils/cache.py index 48a4ae75..be653042 100644 --- a/aymurai/utils/cache.py +++ b/aymurai/utils/cache.py @@ -1,13 +1,12 @@ -import os import json +import os import pickle -from typing import Any, Optional +from typing import Any -import joblib import diskcache +import joblib from aymurai.logger import get_logger -from aymurai.meta.types import DataItem from aymurai.utils.json_encoding import EnhancedJSONEncoder logger = get_logger(__name__) @@ -18,15 +17,15 @@ def flatten_dict(current: dict, key: str = "", result: dict = {}) -> dict: """ - Flatten a dict + Flatten nested dictionaries into a dotted-key mapping. Args: - current (dict): dict to be flattened - key (str, optional): key to be used. Defaults to "". - result (dict, optional): result dict. Defaults to {}. + current (dict): Source dictionary to flatten. + key (str): Parent key prefix. Defaults to "". + result (dict, optional): Accumulator reused across recursion. Defaults to {}. Returns: - dict: flattened dict + dict: Mapping of flattened keys to terminal values. """ if type(current) is dict: for k in current: @@ -37,12 +36,12 @@ def flatten_dict(current: dict, key: str = "", result: dict = {}) -> dict: return result -def cache_clear(keys: list[str]): +def cache_clear(keys: list[str]) -> None: """ - Clear cache + Remove the provided keys from the disk-backed cache. Args: - keys (list[str]): keys to be cleared + keys (list[str]): Cache keys to delete. """ for key in keys: cache.pop(key) @@ -50,14 +49,14 @@ def cache_clear(keys: list[str]): def get_cache_key(item: Any, context: Any = "") -> str: """ - Get cache key + Build a stable cache key combining the item payload and context. Args: - item (Any): Data to hash - context (Any): context object to create hash + item (Any): Value to hash into the key namespace. + context (Any): Additional context used to scope the key. Defaults to "". Returns: - str: hash + str: Deterministic hash suitable for cache lookups. """ if type(item) in [dict]: @@ -72,20 +71,29 @@ def get_cache_key(item: Any, context: Any = "") -> str: return cache_key -def is_cached(key: str): +def is_cached(key: str) -> bool: + """ + Determine whether a cache entry exists for the given key. + + Args: + key (str): Cache key to inspect. + + Returns: + bool: True when the key is present, otherwise False. + """ return key in cache -def cache_save( - data_item: DataItem, - key: str, -): +def cache_save(data_item: Any, key: str) -> None: """ - save data on cache + Persist a Python object in the disk-backed cache. Args: - data (Data): data to be cached - key (str): key to store + data_item (Any): Serializable payload to store. + key (str): Cache key under which the payload is saved. + + Returns: + None: This function does not return a value. """ data = pickle.dumps(data_item) @@ -94,18 +102,16 @@ def cache_save( cache.set(key, data) -def cache_load(key: str) -> Optional[DataItem]: +def cache_load(key: str) -> Any | None: """ - load data from cache + Retrieve and deserialize a cached payload when present. Args: - key (str): key to load + key (str): Cache key to resolve. Returns: - Optional[Data]: loaded data - + Any | None: Cached payload on hit, otherwise ``None``. """ - if key in cache: # Retrieve the serialized object from cache data = cache.get(key) diff --git a/docker-compose.yml b/docker-compose.yml index 9a0b3da7..3876bb91 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,56 +1,111 @@ +x-api-lite: &api-lite + image: ghcr.io/aymurai/api:latest + build: + context: . + dockerfile: ./docker/api/Dockerfile + target: aymurai-api + shm_size: "2gb" + ports: + - "8899:8899" + volumes: + - ./resources/cache:/resources/cache + env_file: + - .env + - .env.common + +x-api-full: &api-full + image: ghcr.io/aymurai/api:full + build: + context: . + dockerfile: ./docker/api/Dockerfile + target: aymurai-api-full + shm_size: "2gb" + ports: + - "8899:8899" + restart: always + +x-ollama: &ollama-base + image: ollama/ollama + ports: + - "11434:11434" + volumes: + - ollama:/root/.ollama + restart: always + shm_size: "2gb" + services: + # CPU-friendly API aymurai-api: - image: ghcr.io/aymurai/api:latest - ports: - - "8899:8899" + <<: *api-lite depends_on: - ollama - build: - context: . - dockerfile: ./docker/api/Dockerfile - target: aymurai-api - env_file: - - .env - - .env.common environment: OLLAMA_HOST: http://ollama:11434 - volumes: - - ./resources/cache:/resources/cache + TORCH_DEVICE: cpu + # GPU-enabled API + aymurai-api-gpu: + <<: *api-lite + depends_on: + - ollama-gpu + environment: + OLLAMA_HOST: http://ollama-gpu:11434 + TORCH_DEVICE: cuda + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [ gpu ] + + # CPU-friendly Full API aymurai-api-full: - image: ghcr.io/aymurai/api:full - ports: - - "8899:8899" + <<: *api-full depends_on: - ollama - build: - context: . - dockerfile: ./docker/api/Dockerfile - target: aymurai-api-full - restart: always environment: OLLAMA_HOST: http://ollama:11434 + TORCH_DEVICE: cpu deploy: resources: limits: memory: 4g cpus: "4.0" + # GPU-enabled Full API + aymurai-api-full-gpu: + <<: *api-full + depends_on: + - ollama-gpu + environment: + OLLAMA_HOST: http://ollama-gpu:11434 + TORCH_DEVICE: cuda + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [ gpu ] + limits: + memory: 4g + cpus: "4.0" + + # CPU-only Ollama (default) ollama: - image: ollama/ollama - container_name: ollama + <<: *ollama-base + + # GPU-enabled Ollama (run instead on GPU hosts) + ollama-gpu: + <<: *ollama-base deploy: resources: reservations: devices: - driver: nvidia count: all - capabilities: [gpu] - ports: - - "11434:11434" - volumes: - - ollama:/root/.ollama - restart: always + capabilities: [ gpu ] volumes: ollama: diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index 660e5a7e..2462d4ec 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -33,7 +33,6 @@ RUN --mount=type=bind,source=.git,target=.git \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-editable - # ------------------- # Base API Stage # ------------------- @@ -75,6 +74,7 @@ COPY --from=builder --chown=app:app /app/.venv /app/.venv # Copy static resources COPY resources/api/static /resources/api/static COPY resources/pipelines /resources/pipelines +COPY resources/llm /resources/llm # Set working directory WORKDIR /app @@ -96,6 +96,7 @@ ENV TF_CPP_MIN_LOG_LEVEL=3 \ # Copy additional resources COPY ./resources/api resources/api COPY ./resources/pipelines/production resources/pipelines/production +COPY ./resources/llm resources/llm # Initialize application to download models RUN uv run python /app/.venv/lib/python3.10/site-packages/aymurai/api/main.py diff --git a/pyproject.toml b/pyproject.toml index 26c99114..37d72eb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,13 +57,12 @@ dependencies = [ "odfpy>=1.4.1", "gdown==4.6.0", "joblib>=1.4.2", - "textract==1.6.5", "datetime_matcher @ git+https://github.com/jedzill4/datetime_matcher", "jiwer==3.0.5", "datasets>=3.2.0", "unidecode==1.3.8", "sentencepiece==0.2.0", - "flair==0.14.0", + "flair==0.15.1", "fastapi[standard]>=0.115.6", "uvicorn>=0.34.0", "python-multipart>=0.0.20", @@ -74,8 +73,7 @@ dependencies = [ "cachetools>=5.5.0", "diskcache>=5.6.3", "scipy<1.14.1", - "torch==1.12.1", - "torchtext==0.13.1", + "torch>=1.13.1", "pytorch-lightning==1.8.3.post1", "tensorflow_text==2.10.0; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'x86_64') or sys_platform == 'win32'", # no ARM64 macOS wheels "psutil==6.1.0", @@ -92,6 +90,8 @@ dependencies = [ "langextract[openai]==1.1.0", "ollama==0.6.1", "tiktoken==0.12.0", + "marker-pdf==1.10.1", + "docx2txt>=0.9", ] [project.urls] diff --git a/resources/pipelines/production/full-paragraph/pipeline.json b/resources/pipelines/production/full-paragraph/pipeline.json index a60309b5..b3636d2a 100644 --- a/resources/pipelines/production/full-paragraph/pipeline.json +++ b/resources/pipelines/production/full-paragraph/pipeline.json @@ -14,16 +14,6 @@ "device": "cpu", "use_tokenizer": false } - ], - [ - "aymurai.models.decision.binregex.DecisionConv1dBinRegex", - { - "tokenizer_path": "https://drive.google.com/uc?id=1eljQOinpObdfBREIKxVnC5Y2g_sbhPHT&confirm=true", - "model_checkpoint": "https://drive.google.com/uc?id=19_YmBJnO06iS0qW8ak0zl0EIsJYin8kQ&confirm=true", - "device": "cpu", - "threshold": 0.5, - "return_only_with_detalle": true - } ] ], "postprocess": [ diff --git a/test/text/test_extraction.py b/test/text/test_extraction.py new file mode 100644 index 00000000..5e4dba56 --- /dev/null +++ b/test/text/test_extraction.py @@ -0,0 +1,94 @@ +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from aymurai.text.extraction import ERRORS, InvalidFile, extract_document, get_extension + + +class ExtractionTestCase(unittest.TestCase): + def setUp(self) -> None: + self.tempdir = tempfile.TemporaryDirectory() + self.addCleanup(self.tempdir.cleanup) + self.tmp_path = Path(self.tempdir.name) + + def test_extract_document_dispatches(self): + source = self.tmp_path / "document.docx" + source.write_text("dummy") + + class DummyExtractor: + def __init__(self) -> None: + self.called = False + self.called_path: Path | None = None + + def extract(self, path: Path) -> str: + self.called = True + self.called_path = path + return "ok" + + dummy = DummyExtractor() + + with patch( + "aymurai.text.extraction.get_extractor", + return_value=dummy, + ): + result = extract_document(source) + + self.assertEqual(result, "ok") + self.assertTrue(dummy.called) + self.assertEqual(dummy.called_path, source) + + def test_extract_document_missing_file_raises(self): + missing = Path("/no/such/file.pdf") + with self.assertRaises(InvalidFile): + extract_document(missing, errors="raise") + + def test_extract_document_handles_invalid_file(self): + source = self.tmp_path / "document.pdf" + source.write_text("dummy") + + class BoomExtractor: + def extract(self, _path: Path) -> str: + raise InvalidFile("boom") + + with patch( + "aymurai.text.extraction.get_extractor", + return_value=BoomExtractor(), + ): + result = extract_document(source, errors="ignore") + + self.assertIsNone(result) + + def test_extract_document_raises_unexpected(self): + source = self.tmp_path / "document.odt" + source.write_text("dummy") + + class BoomExtractor: + def extract(self, _path: Path) -> str: + raise RuntimeError("boom") + + with patch( + "aymurai.text.extraction.get_extractor", + return_value=BoomExtractor(), + ): + with self.assertRaises(RuntimeError): + extract_document(source, errors="raise") + + def test_get_extension_basic(self): + cases = [ + ("file.pdf", "pdf"), + ("file.docx", "docx"), + ("file.odt", "odt"), + ("file.unknown", "unknown"), + ] + + for filename, expected in cases: + with self.subTest(filename=filename): + self.assertEqual(get_extension(filename), expected) + + def test_errors_configured(self): + self.assertEqual(set(ERRORS), {"ignore", "coerce", "raise"}) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/text/test_extractors.py b/test/text/test_extractors.py new file mode 100644 index 00000000..8beed5ff --- /dev/null +++ b/test/text/test_extractors.py @@ -0,0 +1,109 @@ +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from aymurai.text.extractors.base import InvalidFile +from aymurai.text.extractors.docx import DocxExtractor +from aymurai.text.extractors.odt import OdtExtractor +from aymurai.text.extractors.pdf import PdfExtractor + + +class ExtractorTestCase(unittest.TestCase): + def setUp(self) -> None: + self.tempdir = tempfile.TemporaryDirectory() + self.addCleanup(self.tempdir.cleanup) + self.tmp_path = Path(self.tempdir.name) + + def test_docx_extractor_appends_footnotes(self): + file_path = self.tmp_path / "sample.docx" + file_path.write_bytes(b"fake docx content") + + with ( + patch( + "aymurai.text.extractors.docx.docx2txt.process", + return_value="Document body", + ), + patch( + "aymurai.text.extractors.docx.get_footnotes", + return_value=["Footnote one", ""], + ), + ): + extractor = DocxExtractor() + result = extractor.extract(file_path) + + self.assertIn("Document body", result) + self.assertTrue(result.strip().endswith("Footnote one")) + + def test_docx_extractor_raises_invalid_file(self): + file_path = self.tmp_path / "broken.docx" + file_path.write_bytes(b"") + + with patch( + "aymurai.text.extractors.docx.docx2txt.process", + side_effect=OSError("boom"), + ): + extractor = DocxExtractor() + with self.assertRaises(InvalidFile): + extractor.extract(file_path) + + def test_odt_extractor_prepends_header(self): + file_path = self.tmp_path / "sample.odt" + file_path.write_bytes(b"fake odt content") + + with ( + patch( + "aymurai.text.extractors.odt.odt_to_text", + return_value="Paragraph one", + ), + patch( + "aymurai.text.extractors.odt.get_header", + return_value=["Header"], + ), + ): + extractor = OdtExtractor() + result = extractor.extract(file_path) + + self.assertTrue(result.startswith("Header")) + self.assertIn("Paragraph one", result) + + def test_odt_extractor_invalid_file(self): + file_path = self.tmp_path / "broken.odt" + file_path.write_bytes(b"") + + with patch( + "aymurai.text.extractors.odt.odt_to_text", + side_effect=ValueError("bad xml"), + ): + extractor = OdtExtractor() + with self.assertRaises(InvalidFile): + extractor.extract(file_path) + + def test_pdf_extractor_delegates(self): + file_path = self.tmp_path / "sample.pdf" + file_path.write_bytes(b"fake pdf") + + with patch( + "aymurai.text.extractors.pdf.pdf_to_text", + return_value="PDF text", + ): + extractor = PdfExtractor() + result = extractor.extract(file_path) + + self.assertEqual(result, "PDF text") + + def test_pdf_extractor_wraps_errors(self): + file_path = self.tmp_path / "broken.pdf" + file_path.write_bytes(b"") + + with patch( + "aymurai.text.extractors.pdf.pdf_to_text", + side_effect=ValueError("invalid"), + ): + extractor = PdfExtractor() + with self.assertRaises(InvalidFile): + extractor.extract(file_path) + + +if __name__ == "__main__": + unittest.main() diff --git a/uv.lock b/uv.lock index 594c7511..3acc56ea 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,10 @@ version = 1 requires-python = "==3.10.*" +resolution-markers = [ + "platform_system == 'Darwin'", + "platform_machine == 'aarch64' and platform_system == 'Linux'", + "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')", +] [[package]] name = "absl-py" @@ -135,6 +140,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] +[[package]] +name = "anthropic" +version = "0.46.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228 }, +] + [[package]] name = "anyio" version = "4.12.0" @@ -158,15 +181,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, ] -[[package]] -name = "argcomplete" -version = "1.10.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/28/07d2cfe0838f998ea2eafab59f52b0ceb1e70adb1831fa14b958a9fa6c5c/argcomplete-1.10.3.tar.gz", hash = "sha256:a37f522cf3b6a34abddfedb61c4546f60023b3799b22d1cd971eacdc0861530a", size = 50173 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/8e/6b293f883fdbd29b9c8170db44bddff9e7de224d8cf1eb4287f69f1766e5/argcomplete-1.10.3-py2.py3-none-any.whl", hash = "sha256:d8ea63ebaec7f59e56e7b2a386b1d1c7f1a7ae87902c9ee17d377eaa557f06fa", size = 36576 }, -] - [[package]] name = "argon2-cffi" version = "25.1.0" @@ -284,7 +298,7 @@ wheels = [ [[package]] name = "aymurai" -version = "1.1.13.dev32" +version = "2.0.0a2.dev9" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -292,6 +306,7 @@ dependencies = [ { name = "datasets" }, { name = "datetime-matcher" }, { name = "diskcache" }, + { name = "docx2txt" }, { name = "faker" }, { name = "fastapi", extra = ["standard"] }, { name = "flair" }, @@ -299,6 +314,7 @@ dependencies = [ { name = "jiwer" }, { name = "joblib" }, { name = "langextract", extra = ["openai"] }, + { name = "marker-pdf" }, { name = "more-itertools" }, { name = "numpy" }, { name = "odfpy" }, @@ -321,10 +337,8 @@ dependencies = [ { name = "tenacity" }, { name = "tensorflow-hub" }, { name = "tensorflow-text", marker = "(platform_machine == 'x86_64' and sys_platform == 'darwin') or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "textract" }, { name = "tiktoken" }, { name = "torch" }, - { name = "torchtext" }, { name = "unidecode" }, { name = "uvicorn" }, { name = "xmltodict" }, @@ -352,13 +366,15 @@ requires-dist = [ { name = "datasets", specifier = ">=3.2.0" }, { name = "datetime-matcher", git = "https://github.com/jedzill4/datetime_matcher" }, { name = "diskcache", specifier = ">=5.6.3" }, + { name = "docx2txt", specifier = ">=0.9" }, { name = "faker", specifier = "==18.11.2" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.6" }, - { name = "flair", specifier = "==0.14.0" }, + { name = "flair", specifier = "==0.15.1" }, { name = "gdown", specifier = "==4.6.0" }, { name = "jiwer", specifier = "==3.0.5" }, { name = "joblib", specifier = ">=1.4.2" }, { name = "langextract", extras = ["openai"], specifier = "==1.1.0" }, + { name = "marker-pdf", specifier = "==1.10.1" }, { name = "more-itertools", specifier = ">=10.5.0" }, { name = "numpy", specifier = "<2.0.0" }, { name = "odfpy", specifier = ">=1.4.1" }, @@ -381,10 +397,8 @@ requires-dist = [ { name = "tenacity", specifier = ">=9.0.0" }, { name = "tensorflow-hub", specifier = ">=0.16.1" }, { name = "tensorflow-text", marker = "(platform_machine == 'x86_64' and sys_platform == 'darwin') or sys_platform == 'linux' or sys_platform == 'win32'", specifier = "==2.10.0" }, - { name = "textract", specifier = "==1.6.5" }, { name = "tiktoken", specifier = "==0.12.0" }, - { name = "torch", specifier = "==1.12.1" }, - { name = "torchtext", specifier = "==0.13.1" }, + { name = "torch", specifier = ">=1.13.1" }, { name = "unidecode", specifier = "==1.3.8" }, { name = "uvicorn", specifier = ">=0.34.0" }, { name = "xmltodict", specifier = "==0.14.2" }, @@ -425,14 +439,15 @@ wheels = [ [[package]] name = "beautifulsoup4" -version = "4.8.2" +version = "4.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/ba/0e121661f529e7f456e903bf5c4d255b8051d8ce2b5e629c5212efe4c3f1/beautifulsoup4-4.8.2.tar.gz", hash = "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a", size = 298650 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a1/c698cf319e9cfed6b17376281bd0efc6bfc8465698f54170ef60a485ab5d/beautifulsoup4-4.8.2-py3-none-any.whl", hash = "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887", size = 106874 }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, ] [[package]] @@ -555,15 +570,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, ] -[[package]] -name = "chardet" -version = "3.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", size = 1868453 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", size = 133356 }, -] - [[package]] name = "charset-normalizer" version = "3.4.4" @@ -624,7 +630,7 @@ name = "colorlog" version = "6.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162 } wheels = [ @@ -640,15 +646,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, ] -[[package]] -name = "compressed-rtf" -version = "1.0.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/0c/929a4e8ef9d7143f54d77dadb5f370cc7b98534b1bd6e1124d0abe8efb24/compressed_rtf-1.0.7.tar.gz", hash = "sha256:7c30859334839f3cdc7d10796af5b434bb326b9df7cb5a65e95a8eacb2951b0e", size = 8152 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/1d/62f5bf92e12335eb63517f42671ed78512d48bbc69e02a942dd7b90f03f0/compressed_rtf-1.0.7-py3-none-any.whl", hash = "sha256:b7904921d78c67a0a4b7fff9fb361a00ae2b447b6edca010ce321cd98fa0fcc0", size = 7968 }, -] - [[package]] name = "conllu" version = "4.5.3" @@ -859,11 +856,12 @@ wheels = [ ] [[package]] -name = "ebcdic" -version = "1.1.1" +name = "einops" +version = "0.8.1" source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/2f/633031205333bee5f9f93761af8268746aa75f38754823aabb8570eb245b/ebcdic-1.1.1-py2.py3-none-any.whl", hash = "sha256:33b4cb729bc2d0bf46cc1847b0e5946897cb8d3f53520c5b9aa5fa98d7e735f1", size = 128537 }, + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, ] [[package]] @@ -909,22 +907,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, ] -[[package]] -name = "extract-msg" -version = "0.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "compressed-rtf" }, - { name = "ebcdic" }, - { name = "imapclient" }, - { name = "olefile" }, - { name = "tzlocal" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/70/60c17682d8b95077c526fe8374061f309fa647836f7783ee14b1277a9d9b/extract_msg-0.29.0.tar.gz", hash = "sha256:ae6ce5f78fddb582350cb49bbf2776eadecdbf3c74b7a305dced42bd187a5401", size = 72891 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/dc/511f62860fc076fc4e27bfbb1bc6b1f2b61e694d68007853d983d1877bdf/extract_msg-0.29.0-py2.py3-none-any.whl", hash = "sha256:a8885dc385d0c88c4b87fb2a573727c0115cd2ef5157956cf183878f940eef28", size = 72912 }, -] - [[package]] name = "faker" version = "18.11.2" @@ -1073,6 +1055,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 }, ] +[[package]] +name = "filetype" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970 }, +] + [[package]] name = "fire" version = "0.7.1" @@ -1087,7 +1078,7 @@ wheels = [ [[package]] name = "flair" -version = "0.14.0" +version = "0.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bioc" }, @@ -1108,7 +1099,6 @@ dependencies = [ { name = "regex" }, { name = "scikit-learn" }, { name = "segtok" }, - { name = "semver" }, { name = "sqlitedict" }, { name = "tabulate" }, { name = "torch" }, @@ -1117,9 +1107,9 @@ dependencies = [ { name = "transformers", extra = ["sentencepiece"] }, { name = "wikipedia-api" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/3d/81fedb2222c9b9f6b922b69166f53f6244f6e91037ed0a8e121acd16ceef/flair-0.14.0.tar.gz", hash = "sha256:dc14b58e93a52141f204d9995191aa3b7e0463a661a41faa8f8db30745a188a4", size = 371405 } +sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607 } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/f2/29a37585be8e824157d17c6196947b86a7312b0184a5d84f6792082130f5/flair-0.14.0-py3-none-any.whl", hash = "sha256:10735065ff462c05d0ea06ff01bc90e647f822a287858f9a6d0dabc7e402c754", size = 776453 }, + { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, ] [[package]] @@ -1570,18 +1560,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, ] -[[package]] -name = "imapclient" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/31/883f78210ed7578f6dd41e4dbc3ad5e7c6127a51e56513b8b7bb7efdf9b3/IMAPClient-2.1.0.zip", hash = "sha256:60ba79758cc9f13ec910d7a3df9acaaf2bb6c458720d9a02ec33a41352fd1b99", size = 248423 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/39/e1c2c2c6e2356ab6ea81fcfc0a74b044b311d6a91a45300811d9a6077ef7/IMAPClient-2.1.0-py2.py3-none-any.whl", hash = "sha256:3eeb97b9aa8faab0caa5024d74bfde59408fbd542781246f6960873c7bf0dd01", size = 73972 }, -] - [[package]] name = "importlib-metadata" version = "8.7.0" @@ -1632,7 +1610,7 @@ name = "ipython" version = "8.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, { name = "decorator" }, { name = "exceptiongroup" }, { name = "jedi" }, @@ -1952,7 +1930,7 @@ dependencies = [ { name = "overrides" }, { name = "packaging" }, { name = "prometheus-client" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux')" }, { name = "pyzmq" }, { name = "send2trash" }, { name = "terminado" }, @@ -1970,7 +1948,7 @@ name = "jupyter-server-terminals" version = "0.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux')" }, { name = "terminado" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430 } @@ -2167,13 +2145,12 @@ wheels = [ [[package]] name = "litellm" -version = "1.80.7" +version = "1.80.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, { name = "fastuuid" }, - { name = "grpcio" }, { name = "httpx" }, { name = "importlib-metadata" }, { name = "jinja2" }, @@ -2184,9 +2161,9 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/3f/af532014449c3931ae6cad2d97d267dd43d0de006060a8cbf0962e004024/litellm-1.80.7.tar.gz", hash = "sha256:3977a8d195aef842d01c18bf9e22984829363c6a4b54daf9a43c9dd9f190b42c", size = 12023127 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/e0/2e60a0c09235fd7b55297390c557923f3c35a9cf001914222c26a7857d2b/litellm-1.80.7-py3-none-any.whl", hash = "sha256:f7d993f78c1e0e4e1202b2a925cc6540b55b6e5fb055dd342d88b145ab3102ed", size = 10848321 }, + { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975 }, ] [[package]] @@ -2260,6 +2237,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, ] +[[package]] +name = "markdown2" +version = "2.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954 }, +] + +[[package]] +name = "markdownify" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724 }, +] + +[[package]] +name = "marker-pdf" +version = "1.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anthropic" }, + { name = "click" }, + { name = "filetype" }, + { name = "ftfy" }, + { name = "google-genai" }, + { name = "markdown2" }, + { name = "markdownify" }, + { name = "openai" }, + { name = "pdftext" }, + { name = "pillow" }, + { name = "pre-commit" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "rapidfuzz" }, + { name = "regex" }, + { name = "scikit-learn" }, + { name = "surya-ocr" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914 }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -2375,6 +2406,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, ] +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + [[package]] name = "multidict" version = "6.7.0" @@ -2507,6 +2547,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, ] +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + [[package]] name = "nodeenv" version = "1.9.1" @@ -2560,6 +2609,155 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, + { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, + { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, + { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.3.20" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/9d/3dd98852568fb845ec1f7902c90a22b240fe1cbabda411ccedf2fd737b7b/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b0b960da3842212758e4fa4696b94f129090b30e5122fea3c5345916545cff0", size = 124484616 }, + { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, +] + [[package]] name = "oauthlib" version = "3.3.1" @@ -2578,15 +2776,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045 } -[[package]] -name = "olefile" -version = "0.47" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/1b/077b508e3e500e1629d366249c3ccb32f95e50258b231705c09e3c7a4366/olefile-0.47.zip", hash = "sha256:599383381a0bf3dfbd932ca0ca6515acd174ed48870cbf7fee123d698c192c1c", size = 112240 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/d3/b64c356a907242d719fc668b71befd73324e47ab46c8ebbbede252c154b2/olefile-0.47-py2.py3-none-any.whl", hash = "sha256:543c7da2a7adadf21214938bb79c83ea12b473a4b6ee4ad4bf854e7715e13d1f", size = 114565 }, -] - [[package]] name = "ollama" version = "0.6.1" @@ -2602,7 +2791,7 @@ wheels = [ [[package]] name = "openai" -version = "2.9.0" +version = "1.109.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2614,9 +2803,26 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/48/516290f38745cc1e72856f50e8afed4a7f9ac396a5a18f39e892ab89dfc2/openai-2.9.0.tar.gz", hash = "sha256:b52ec65727fc8f1eed2fbc86c8eac0998900c7ef63aa2eb5c24b69717c56fa5f", size = 608202 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133 } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/fd/ae2da789cd923dd033c99b8d544071a827c92046b150db01cfa5cea5b3fd/openai-2.9.0-py3-none-any.whl", hash = "sha256:0d168a490fbb45630ad508a6f3022013c155a68fd708069b6a1a01a5e8f0ffad", size = 1030836 }, + { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627 }, +] + +[[package]] +name = "opencv-python-headless" +version = "4.11.0.86" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, + { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, + { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060 }, + { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856 }, + { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425 }, + { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, ] [[package]] @@ -2723,18 +2929,18 @@ wheels = [ ] [[package]] -name = "pdfminer-six" -version = "20191110" +name = "pdftext" +version = "0.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "chardet" }, - { name = "pycryptodome" }, - { name = "six" }, - { name = "sortedcontainers" }, + { name = "click" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pypdfium2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/31/7acc148333749d6a8ef7cbf25902bdf59a462811a69d040a9a259916b6bd/pdfminer.six-20191110.tar.gz", hash = "sha256:141a53ec491bee6d45bf9b2c7f82601426fb5d32636bcf6b9c8a8f3b6431fea6", size = 10280313 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/83/200b2723bcbf1d1248a8a7d16e6dd6cb970b5331397b11948428d7ebcf37/pdfminer.six-20191110-py2.py3-none-any.whl", hash = "sha256:ca2ca58f3ac66a486bce53a6ddba95dc2b27781612915fa41c444790ba9cd2a8", size = 5606096 }, + { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693 }, ] [[package]] @@ -2751,21 +2957,28 @@ wheels = [ [[package]] name = "pillow" -version = "12.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/08/26e68b6b5da219c2a2cb7b563af008b53bb8e6b6fcb3fa40715fcdb2523a/pillow-12.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:3adfb466bbc544b926d50fe8f4a4e6abd8c6bffd28a26177594e6e9b2b76572b", size = 5289809 }, - { url = "https://files.pythonhosted.org/packages/cb/e9/4e58fb097fb74c7b4758a680aacd558810a417d1edaa7000142976ef9d2f/pillow-12.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ac11e8ea4f611c3c0147424eae514028b5e9077dd99ab91e1bd7bc33ff145e1", size = 4650606 }, - { url = "https://files.pythonhosted.org/packages/4b/e0/1fa492aa9f77b3bc6d471c468e62bfea1823056bf7e5e4f1914d7ab2565e/pillow-12.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d49e2314c373f4c2b39446fb1a45ed333c850e09d0c59ac79b72eb3b95397363", size = 6221023 }, - { url = "https://files.pythonhosted.org/packages/c1/09/4de7cd03e33734ccd0c876f0251401f1314e819cbfd89a0fcb6e77927cc6/pillow-12.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7b2a63fd6d5246349f3d3f37b14430d73ee7e8173154461785e43036ffa96ca", size = 8024937 }, - { url = "https://files.pythonhosted.org/packages/2e/69/0688e7c1390666592876d9d474f5e135abb4acb39dcb583c4dc5490f1aff/pillow-12.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d64317d2587c70324b79861babb9c09f71fbb780bad212018874b2c013d8600e", size = 6334139 }, - { url = "https://files.pythonhosted.org/packages/ed/1c/880921e98f525b9b44ce747ad1ea8f73fd7e992bafe3ca5e5644bf433dea/pillow-12.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d77153e14b709fd8b8af6f66a3afbb9ed6e9fc5ccf0b6b7e1ced7b036a228782", size = 7026074 }, - { url = "https://files.pythonhosted.org/packages/28/03/96f718331b19b355610ef4ebdbbde3557c726513030665071fd025745671/pillow-12.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32ed80ea8a90ee3e6fa08c21e2e091bba6eda8eccc83dbc34c95169507a91f10", size = 6448852 }, - { url = "https://files.pythonhosted.org/packages/3a/a0/6a193b3f0cc9437b122978d2c5cbce59510ccf9a5b48825096ed7472da2f/pillow-12.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c828a1ae702fc712978bda0320ba1b9893d99be0badf2647f693cc01cf0f04fa", size = 7117058 }, - { url = "https://files.pythonhosted.org/packages/a7/c4/043192375eaa4463254e8e61f0e2ec9a846b983929a8d0a7122e0a6d6fff/pillow-12.0.0-cp310-cp310-win32.whl", hash = "sha256:bd87e140e45399c818fac4247880b9ce719e4783d767e030a883a970be632275", size = 6295431 }, - { url = "https://files.pythonhosted.org/packages/92/c6/c2f2fc7e56301c21827e689bb8b0b465f1b52878b57471a070678c0c33cd/pillow-12.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:455247ac8a4cfb7b9bc45b7e432d10421aea9fc2e74d285ba4072688a74c2e9d", size = 7000412 }, - { url = "https://files.pythonhosted.org/packages/b2/d2/5f675067ba82da7a1c238a73b32e3fd78d67f9d9f80fbadd33a40b9c0481/pillow-12.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6ace95230bfb7cd79ef66caa064bbe2f2a1e63d93471c3a2e1f1348d9f22d6b7", size = 2435903 }, +version = "10.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, ] [[package]] @@ -2988,32 +3201,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, ] -[[package]] -name = "pycryptodome" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627 }, - { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362 }, - { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625 }, - { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954 }, - { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534 }, - { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853 }, - { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465 }, - { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414 }, - { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484 }, - { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636 }, - { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675 }, - { url = "https://files.pythonhosted.org/packages/9f/7c/f5b0556590e7b4e710509105e668adb55aa9470a9f0e4dea9c40a4a11ce1/pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56", size = 1705791 }, - { url = "https://files.pythonhosted.org/packages/33/38/dcc795578d610ea1aaffef4b148b8cafcfcf4d126b1e58231ddc4e475c70/pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7", size = 1780265 }, - { url = "https://files.pythonhosted.org/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886 }, - { url = "https://files.pythonhosted.org/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151 }, - { url = "https://files.pythonhosted.org/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461 }, - { url = "https://files.pythonhosted.org/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440 }, - { url = "https://files.pythonhosted.org/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005 }, -] - [[package]] name = "pydantic" version = "2.12.5" @@ -3179,6 +3366,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890 }, ] +[[package]] +name = "pypdfium2" +version = "4.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254 }, + { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624 }, + { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126 }, + { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077 }, + { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431 }, + { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008 }, + { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543 }, + { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911 }, + { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430 }, + { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951 }, + { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098 }, + { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118 }, +] + [[package]] name = "pysocks" version = "1.7.1" @@ -3240,20 +3447,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, ] -[[package]] -name = "python-pptx" -version = "0.6.23" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lxml" }, - { name = "pillow" }, - { name = "xlsxwriter" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/20/e7/aeaf794b2d440da609684494075e64cfada248026ecb265807d0668cdd00/python-pptx-0.6.23.tar.gz", hash = "sha256:587497ff28e779ab18dbb074f6d4052893c85dedc95ed75df319364f331fedee", size = 10083771 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/49/6eee83072983473e9905ffddd5c2032b9a0ca4616425560d6d582287b467/python_pptx-0.6.23-py3-none-any.whl", hash = "sha256:dd0527194627a2b7cc05f3ba23ecaa2d9a0d5ac9b6193a28ed1b7a716f4217d4", size = 471575 }, -] - [[package]] name = "pytorch-lightning" version = "1.8.3.post1" @@ -3404,25 +3597,26 @@ wheels = [ [[package]] name = "regex" -version = "2025.11.3" +version = "2024.11.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/d6/d788d52da01280a30a3f6268aef2aa71043bff359c618fea4c5b536654d5/regex-2025.11.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2b441a4ae2c8049106e8b39973bfbddfb25a179dda2bdb99b0eeb60c40a6a3af", size = 488087 }, - { url = "https://files.pythonhosted.org/packages/69/39/abec3bd688ec9bbea3562de0fd764ff802976185f5ff22807bf0a2697992/regex-2025.11.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2fa2eed3f76677777345d2f81ee89f5de2f5745910e805f7af7386a920fa7313", size = 290544 }, - { url = "https://files.pythonhosted.org/packages/39/b3/9a231475d5653e60002508f41205c61684bb2ffbf2401351ae2186897fc4/regex-2025.11.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8b4a27eebd684319bdf473d39f1d79eed36bf2cd34bd4465cdb4618d82b3d56", size = 288408 }, - { url = "https://files.pythonhosted.org/packages/c3/c5/1929a0491bd5ac2d1539a866768b88965fa8c405f3e16a8cef84313098d6/regex-2025.11.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cf77eac15bd264986c4a2c63353212c095b40f3affb2bc6b4ef80c4776c1a28", size = 781584 }, - { url = "https://files.pythonhosted.org/packages/ce/fd/16aa16cf5d497ef727ec966f74164fbe75d6516d3d58ac9aa989bc9cdaad/regex-2025.11.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b7f9ee819f94c6abfa56ec7b1dbab586f41ebbdc0a57e6524bd5e7f487a878c7", size = 850733 }, - { url = "https://files.pythonhosted.org/packages/e6/49/3294b988855a221cb6565189edf5dc43239957427df2d81d4a6b15244f64/regex-2025.11.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:838441333bc90b829406d4a03cb4b8bf7656231b84358628b0406d803931ef32", size = 898691 }, - { url = "https://files.pythonhosted.org/packages/14/62/b56d29e70b03666193369bdbdedfdc23946dbe9f81dd78ce262c74d988ab/regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe6d3f0c9e3b7e8c0c694b24d25e677776f5ca26dce46fd6b0489f9c8339391", size = 791662 }, - { url = "https://files.pythonhosted.org/packages/15/fc/e4c31d061eced63fbf1ce9d853975f912c61a7d406ea14eda2dd355f48e7/regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2ab815eb8a96379a27c3b6157fcb127c8f59c36f043c1678110cea492868f1d5", size = 782587 }, - { url = "https://files.pythonhosted.org/packages/b2/bb/5e30c7394bcf63f0537121c23e796be67b55a8847c3956ae6068f4c70702/regex-2025.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:728a9d2d173a65b62bdc380b7932dd8e74ed4295279a8fe1021204ce210803e7", size = 774709 }, - { url = "https://files.pythonhosted.org/packages/c5/c4/fce773710af81b0cb37cb4ff0947e75d5d17dee304b93d940b87a67fc2f4/regex-2025.11.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:509dc827f89c15c66a0c216331260d777dd6c81e9a4e4f830e662b0bb296c313", size = 845773 }, - { url = "https://files.pythonhosted.org/packages/7b/5e/9466a7ec4b8ec282077095c6eb50a12a389d2e036581134d4919e8ca518c/regex-2025.11.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:849202cd789e5f3cf5dcc7822c34b502181b4824a65ff20ce82da5524e45e8e9", size = 836164 }, - { url = "https://files.pythonhosted.org/packages/95/18/82980a60e8ed1594eb3c89eb814fb276ef51b9af7caeab1340bfd8564af6/regex-2025.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b6f78f98741dcc89607c16b1e9426ee46ce4bf31ac5e6b0d40e81c89f3481ea5", size = 779832 }, - { url = "https://files.pythonhosted.org/packages/03/cc/90ab0fdbe6dce064a42015433f9152710139fb04a8b81b4fb57a1cb63ffa/regex-2025.11.3-cp310-cp310-win32.whl", hash = "sha256:149eb0bba95231fb4f6d37c8f760ec9fa6fabf65bab555e128dde5f2475193ec", size = 265802 }, - { url = "https://files.pythonhosted.org/packages/34/9d/e9e8493a85f3b1ddc4a5014465f5c2b78c3ea1cbf238dcfde78956378041/regex-2025.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:ee3a83ce492074c35a74cc76cf8235d49e77b757193a5365ff86e3f2f93db9fd", size = 277722 }, - { url = "https://files.pythonhosted.org/packages/15/c4/b54b24f553966564506dbf873a3e080aef47b356a3b39b5d5aba992b50db/regex-2025.11.3-cp310-cp310-win_arm64.whl", hash = "sha256:38af559ad934a7b35147716655d4a2f79fcef2d695ddfe06a06ba40ae631fa7e", size = 270289 }, + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, ] [[package]] @@ -3688,15 +3882,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332 }, ] -[[package]] -name = "semver" -version = "3.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912 }, -] - [[package]] name = "send2trash" version = "1.8.3" @@ -3774,11 +3959,11 @@ wheels = [ [[package]] name = "six" -version = "1.12.0" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dd/bf/4138e7bfb757de47d1f4b6994648ec67a51efe58fa907c1e11e350cddfca/six-1.12.0.tar.gz", hash = "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73", size = 32725 } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl", hash = "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", size = 10586 }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] [[package]] @@ -3817,14 +4002,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 }, ] -[[package]] -name = "speechrecognition" -version = "3.8.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/e1/7f5678cd94ec1234269d23756dbdaa4c8cfaed973412f88ae8adf7893a50/SpeechRecognition-3.8.1-py2.py3-none-any.whl", hash = "sha256:4d8f73a0c05ec70331c3bacaa89ecc06dfa8d9aba0899276664cda06ab597e8e", size = 32833456 }, -] - [[package]] name = "sqlalchemy" version = "2.0.44" @@ -3926,6 +4103,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/fa/5b010610aa136c44a20de6ed9ec0b36867daf7322d0e3c8e626a99fab11a/streamlit-1.21.0-py2.py3-none-any.whl", hash = "sha256:05862598952a9126e16652caa5bc88eeeea7b2773af252e2daccf1c84ceaaef4", size = 9665093 }, ] +[[package]] +name = "surya-ocr" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "einops" }, + { name = "filetype" }, + { name = "opencv-python-headless" }, + { name = "pillow" }, + { name = "platformdirs" }, + { name = "pre-commit" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pypdfium2" }, + { name = "python-dotenv" }, + { name = "torch" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395 }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, +] + [[package]] name = "tabulate" version = "0.9.0" @@ -4095,7 +4308,7 @@ version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux')" }, { name = "tornado" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } @@ -4103,27 +4316,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, ] -[[package]] -name = "textract" -version = "1.6.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argcomplete" }, - { name = "beautifulsoup4" }, - { name = "chardet" }, - { name = "docx2txt" }, - { name = "extract-msg" }, - { name = "pdfminer-six" }, - { name = "python-pptx" }, - { name = "six" }, - { name = "speechrecognition" }, - { name = "xlrd" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/81/9f/dd29fcec368f007d44e51f0273489d5172a6d32ed9c796df5054fbb31c9f/textract-1.6.5.tar.gz", hash = "sha256:68f0f09056885821e6c43d8538987518daa94057c306679f2857cc5ee66ad850", size = 17871 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/3e/ac16b6bf28edf78296aea7d0cb416b49ed30282ac8c711662541015ee6f3/textract-1.6.5-py3-none-any.whl", hash = "sha256:0accd78ec42864e3e3827f9ef798ced9aac4727b664303b724a198fed73fa438", size = 23140 }, -] - [[package]] name = "tf-keras" version = "2.15.0" @@ -4175,27 +4367,27 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.21.4" +version = "0.22.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/2f/402986d0823f8d7ca139d969af2917fefaa9b947d1fb32f6168c509f2492/tokenizers-0.21.4.tar.gz", hash = "sha256:fa23f85fbc9a02ec5c6978da172cdcbac23498c3ca9f3645c5c68740ac007880", size = 351253 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123 } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/c6/fdb6f72bf6454f52eb4a2510be7fb0f614e541a2554d6210e370d85efff4/tokenizers-0.21.4-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ccc10a7c3bcefe0f242867dc914fc1226ee44321eb618cfe3019b5df3400133", size = 2863987 }, - { url = "https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60", size = 2732457 }, - { url = "https://files.pythonhosted.org/packages/aa/8f/24f39d7b5c726b7b0be95dca04f344df278a3fe3a4deb15a975d194cbb32/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b376f5a1aee67b4d29032ee85511bbd1b99007ec735f7f35c8a2eb104eade5", size = 3012624 }, - { url = "https://files.pythonhosted.org/packages/58/47/26358925717687a58cb74d7a508de96649544fad5778f0cd9827398dc499/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2107ad649e2cda4488d41dfd031469e9da3fcbfd6183e74e4958fa729ffbf9c6", size = 2939681 }, - { url = "https://files.pythonhosted.org/packages/99/6f/cc300fea5db2ab5ddc2c8aea5757a27b89c84469899710c3aeddc1d39801/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c73012da95afafdf235ba80047699df4384fdc481527448a078ffd00e45a7d9", size = 3247445 }, - { url = "https://files.pythonhosted.org/packages/be/bf/98cb4b9c3c4afd8be89cfa6423704337dc20b73eb4180397a6e0d456c334/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f23186c40395fc390d27f519679a58023f368a0aad234af145e0f39ad1212732", size = 3428014 }, - { url = "https://files.pythonhosted.org/packages/75/c7/96c1cc780e6ca7f01a57c13235dd05b7bc1c0f3588512ebe9d1331b5f5ae/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc88bb34e23a54cc42713d6d98af5f1bf79c07653d24fe984d2d695ba2c922a2", size = 3193197 }, - { url = "https://files.pythonhosted.org/packages/f2/90/273b6c7ec78af547694eddeea9e05de771278bd20476525ab930cecaf7d8/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51b7eabb104f46c1c50b486520555715457ae833d5aee9ff6ae853d1130506ff", size = 3115426 }, - { url = "https://files.pythonhosted.org/packages/91/43/c640d5a07e95f1cf9d2c92501f20a25f179ac53a4f71e1489a3dcfcc67ee/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:714b05b2e1af1288bd1bc56ce496c4cebb64a20d158ee802887757791191e6e2", size = 9089127 }, - { url = "https://files.pythonhosted.org/packages/44/a1/dd23edd6271d4dca788e5200a807b49ec3e6987815cd9d0a07ad9c96c7c2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1340ff877ceedfa937544b7d79f5b7becf33a4cfb58f89b3b49927004ef66f78", size = 9055243 }, - { url = "https://files.pythonhosted.org/packages/21/2b/b410d6e9021c4b7ddb57248304dc817c4d4970b73b6ee343674914701197/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3c1f4317576e465ac9ef0d165b247825a2a4078bcd01cba6b54b867bdf9fdd8b", size = 9298237 }, - { url = "https://files.pythonhosted.org/packages/b7/0a/42348c995c67e2e6e5c89ffb9cfd68507cbaeb84ff39c49ee6e0a6dd0fd2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c212aa4e45ec0bb5274b16b6f31dd3f1c41944025c2358faaa5782c754e84c24", size = 9461980 }, - { url = "https://files.pythonhosted.org/packages/3d/d3/dacccd834404cd71b5c334882f3ba40331ad2120e69ded32cf5fda9a7436/tokenizers-0.21.4-cp39-abi3-win32.whl", hash = "sha256:6c42a930bc5f4c47f4ea775c91de47d27910881902b0f20e4990ebe045a415d0", size = 2329871 }, - { url = "https://files.pythonhosted.org/packages/41/f2/fd673d979185f5dcbac4be7d09461cbb99751554ffb6718d0013af8604cb/tokenizers-0.21.4-cp39-abi3-win_amd64.whl", hash = "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", size = 2507568 }, + { url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318 }, + { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478 }, + { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994 }, + { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141 }, + { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049 }, + { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730 }, + { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560 }, + { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221 }, + { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569 }, + { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599 }, + { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862 }, + { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250 }, + { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003 }, + { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684 }, ] [[package]] @@ -4227,17 +4419,37 @@ wheels = [ [[package]] name = "torch" -version = "1.12.1" +version = "2.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/74/7342c7f21449557a8263c925071a55081edd7e9b641404cfe31d6fb71d3b/torch-1.12.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:9c038662db894a23e49e385df13d47b2a777ffd56d9bcd5b832593fab0a7e286", size = 776338835 }, - { url = "https://files.pythonhosted.org/packages/36/b0/4857929aa28dfe26f7de909ebf002b60499edcd7566441a7433df865f9ba/torch-1.12.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4e1b9c14cf13fd2ab8d769529050629a0e68a6fc5cb8e84b4a3cc1dd8c4fe541", size = 55712361 }, - { url = "https://files.pythonhosted.org/packages/b9/25/fc2111599a038aa6c1c618a7dc9246aabc95f899008949ade31213255a0c/torch-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:e9c8f4a311ac29fc7e8e955cfb7733deb5dbe1bdaabf5d4af2765695824b7e0d", size = 162235663 }, - { url = "https://files.pythonhosted.org/packages/37/72/ef80d39a371a9b4a8aadfb22b141972cc67a7075dae69a9d5b8116505ec0/torch-1.12.1-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:976c3f997cea38ee91a0dd3c3a42322785414748d1761ef926b789dfa97c6134", size = 133811424 }, - { url = "https://files.pythonhosted.org/packages/2f/17/8b557dde1cdb5fbe82f90a3f192046c8e508f106456e12f17c87543e6a42/torch-1.12.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:68104e4715a55c4bb29a85c6a8d57d820e0757da363be1ba680fa8cc5be17b52", size = 49099890 }, + { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681 }, + { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036 }, + { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861 }, + { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222 }, ] [[package]] @@ -4254,24 +4466,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162 }, ] -[[package]] -name = "torchtext" -version = "0.13.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "requests" }, - { name = "torch" }, - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/6f/673e3533a296f135fecfccdf2519ecfba7e16971dca5c3bc7a8cc9cfd064/torchtext-0.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aa59df8f9542674311fc23aca147a34256c936c25840c4b6ee3fee47d511ad17", size = 1775853 }, - { url = "https://files.pythonhosted.org/packages/2f/bd/5f27d604d3dc6421b3c0bd24c28f73eecb091fce9dbdcbe7af314f252cee/torchtext-0.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:492a22727181edf5a33fa18587f3430ffbb366c2ea835e0f4f8a08be8d9e859c", size = 1966034 }, - { url = "https://files.pythonhosted.org/packages/48/4e/56352383c30b75becd5faaff8d404eb86f3a2282d72ae52aaad705bf22bf/torchtext-0.13.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f56359165eb00ea2ff998b67727f87e7fa32665c1a46610c9b5a2d5d581095e5", size = 1910382 }, - { url = "https://files.pythonhosted.org/packages/3f/fd/e3eef8b5d691cdeb42506fc9fec2a64ab1549039cdf9e7545a171e4a694e/torchtext-0.13.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:b5bf4f9da5326b6e74318b4a5035abfaf166b3abd822565be685f44947adb8d3", size = 1832850 }, - { url = "https://files.pythonhosted.org/packages/b8/9d/7f9b786637d664579b66ae7c6742d662a91b8e6a9f55a34e22c20883d801/torchtext-0.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5f8c1b426bda2f22e5bfd118c779c12f8612ed1077e5e3a491242bb4ab7ac4d3", size = 2239348 }, -] - [[package]] name = "tornado" version = "6.5.2" @@ -4327,7 +4521,7 @@ wheels = [ [[package]] name = "transformers" -version = "4.47.1" +version = "4.57.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -4341,9 +4535,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/1a/936aeb4f88112f670b604f5748034568dbc2b9bbb457a8d4518b1a15510a/transformers-4.47.1.tar.gz", hash = "sha256:6c29c05a5f595e278481166539202bf8641281536df1c42357ee58a45d0a564a", size = 8707421 } +sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/3a/8bdab26e09c5a242182b7ba9152e216d5ab4ae2d78c4298eb4872549cd35/transformers-4.47.1-py3-none-any.whl", hash = "sha256:d2f5d19bb6283cd66c893ec7e6d931d6370bbf1cc93633326ff1f41a40046c9c", size = 10133598 }, + { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463 }, ] [package.optional-dependencies] @@ -4356,6 +4550,15 @@ torch = [ { name = "torch" }, ] +[[package]] +name = "triton" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/2e/f95e673222afa2c7f0c687d8913e98fcf2589ef0b1405de76894e37fe18f/triton-3.5.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f63e34dcb32d7bd3a1d0195f60f30d2aee8b08a69a0424189b71017e23dfc3d2", size = 159821655 }, + { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692 }, +] + [[package]] name = "typer" version = "0.20.0" @@ -4456,7 +4659,7 @@ wheels = [ [package.optional-dependencies] standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, { name = "httptools" }, { name = "python-dotenv" }, { name = "pyyaml" }, @@ -4670,24 +4873,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046 }, ] -[[package]] -name = "xlrd" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/05/ec9d4fcbbb74bbf4da9f622b3b61aec541e4eccf31d3c60c5422ec027ce2/xlrd-1.2.0.tar.gz", hash = "sha256:546eb36cee8db40c3eaa46c351e67ffee6eeb5fa2650b71bc4c758a29a1b29b2", size = 554079 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/16/63576a1a001752e34bf8ea62e367997530dc553b689356b9879339cf45a4/xlrd-1.2.0-py2.py3-none-any.whl", hash = "sha256:e551fb498759fa3a5384a94ccd4c3c02eb7c00ea424426e212ac0c57be9dfbde", size = 103251 }, -] - -[[package]] -name = "xlsxwriter" -version = "3.2.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/2c/c06ef49dc36e7954e55b802a8b231770d286a9758b3d936bd1e04ce5ba88/xlsxwriter-3.2.9.tar.gz", hash = "sha256:254b1c37a368c444eac6e2f867405cc9e461b0ed97a3233b2ac1e574efb4140c", size = 215940 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/0c/3662f4a66880196a590b202f0db82d919dd2f89e99a27fadef91c4a33d41/xlsxwriter-3.2.9-py3-none-any.whl", hash = "sha256:9a5db42bc5dff014806c58a20b9eae7322a134abb6fce3c92c181bfb275ec5b3", size = 175315 }, -] - [[package]] name = "xmltodict" version = "0.14.2" From 2ec306b9edc041be3fe8e6fd68c5c5903f983702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:42:53 -0300 Subject: [PATCH 026/101] Feature/remove usem tensorflow deps (#68) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🩹 Ensure consistent entity attributes in reformat_entity function and reorder imports * 📝 Update subcategories exploration notebook * ⚗️ Add TensorFlow deprecation experiment notebook * ♻️ Refactor entity subcategorization: Remove USEMSubcategorizer, add SentenceTransformerSubcategorizer - Removed the USEMSubcategorizer implementation from `usem.py`. - Introduced new Jupyter notebooks for testing and evaluating the SentenceTransformerSubcategorizer. - Updated the pipeline configuration to utilize SentenceTransformerSubcategorizer with local embeddings instead of remote URLs. * ♻️ Refactor download function: Replace gdown with requests for improved file downloading * 🔥 Remove empty peft model module * ➖ Remove TensorFlow and gdown dependencies from pyproject.toml * 📌 Update uv.lock * ♻️ Refactor sentence encoder module: Remove unused dependencies and streamline factory functions * 🔖 Update aymurai package version to 2.0.0a3.dev9 --- .../ar_juz_pcyf_10/labelstudio/utils.py | 8 +- aymurai/datasets/ar_juz_pcyf_10/public.py | 16 +- aymurai/models/peft/__init__.py | 0 aymurai/models/sentence_encoder/__init__.py | 45 + .../models/{usem => sentence_encoder}/base.py | 0 aymurai/models/sentence_encoder/factory.py | 102 + .../sentence_transformers_encoder.py | 2 +- aymurai/models/usem/__init__.py | 48 - aymurai/models/usem/core.py | 60 - aymurai/models/usem/factory.py | 107 - aymurai/models/usem/tensorflow_encoder.py | 81 - .../transforms/entity_subcategories/bm25.py | 101 + .../sentence_transformer.py | 270 ++ .../entity_subcategories/subcategories.py | 638 +++++ .../transforms/entity_subcategories/usem.py | 121 - aymurai/utils/download.py | 47 +- .../02-tensorflow-deprecation.ipynb | 2366 +++++++++++++++++ ...nce-transformer-subcategorizer-smoke.ipynb | 104 + ...ence-transformer-subcategorizer-eval.ipynb | 420 +++ .../subcategories/00-exploration.ipynb | 815 ++++-- pyproject.toml | 3 - .../production/full-paragraph/pipeline.json | 28 +- uv.lock | 329 +-- 23 files changed, 4662 insertions(+), 1049 deletions(-) delete mode 100644 aymurai/models/peft/__init__.py create mode 100644 aymurai/models/sentence_encoder/__init__.py rename aymurai/models/{usem => sentence_encoder}/base.py (100%) create mode 100644 aymurai/models/sentence_encoder/factory.py rename aymurai/models/{usem => sentence_encoder}/sentence_transformers_encoder.py (98%) delete mode 100644 aymurai/models/usem/__init__.py delete mode 100644 aymurai/models/usem/core.py delete mode 100644 aymurai/models/usem/factory.py delete mode 100644 aymurai/models/usem/tensorflow_encoder.py create mode 100644 aymurai/transforms/entity_subcategories/bm25.py create mode 100644 aymurai/transforms/entity_subcategories/sentence_transformer.py create mode 100644 aymurai/transforms/entity_subcategories/subcategories.py delete mode 100644 aymurai/transforms/entity_subcategories/usem.py create mode 100755 notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb create mode 100644 notebooks/experiments/sentence-encoders/03-sentence-transformer-subcategorizer-smoke.ipynb create mode 100644 notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb diff --git a/aymurai/datasets/ar_juz_pcyf_10/labelstudio/utils.py b/aymurai/datasets/ar_juz_pcyf_10/labelstudio/utils.py index 94e896db..4cecf591 100644 --- a/aymurai/datasets/ar_juz_pcyf_10/labelstudio/utils.py +++ b/aymurai/datasets/ar_juz_pcyf_10/labelstudio/utils.py @@ -1,13 +1,13 @@ import re -from glob import glob from copy import deepcopy +from glob import glob from itertools import groupby +from more_itertools import collapse, unzip from numpy import cumsum -from more_itertools import unzip, collapse -from aymurai.meta.types import DataItem from aymurai.meta.entities import Entity +from aymurai.meta.types import DataItem from aymurai.utils.json_data import load_json @@ -43,6 +43,8 @@ def reformat_entity(text: str, span: dict) -> dict: end=span["end"], label=span["labels"][0], text=text[span["start"] : span["end"]], + start_char=span["start"], + end_char=span["end"], context_pre=text[soffset : span["start"]], context_post=text[span["end"] : eoffset], attrs={"aymurai_label": span["labels"][0]}, diff --git a/aymurai/datasets/ar_juz_pcyf_10/public.py b/aymurai/datasets/ar_juz_pcyf_10/public.py index 09b9870e..5c7b3cc6 100644 --- a/aymurai/datasets/ar_juz_pcyf_10/public.py +++ b/aymurai/datasets/ar_juz_pcyf_10/public.py @@ -1,17 +1,17 @@ -import sys import logging -import subprocess +import sys +from collections import UserList +from datetime import date, time from pathlib import Path from typing import Any, Union -from collections import UserList -from datetime import date, time, datetime import datasets import pandas as pd -from aymurai.text.extraction import get_extension from aymurai.datasets.ar_juz_pcyf_10.common import BASE, FIELDS +from aymurai.text.extraction import get_extension from aymurai.utils.cache import cache_load, cache_save, get_cache_key +from aymurai.utils.download import download logging.basicConfig(stream=sys.stdout, level=logging.INFO) logger = logging.getLogger(__name__) @@ -38,10 +38,7 @@ def to_datetime(value): @staticmethod def get_file(source: str, dest: str) -> str: - # gdown have to much verbosity and cant be quiet (at least in this version) - # as workaround we use subprocess to get all output (even errors) - cmd = f"gdown --fuzzy -q --continue -O {dest} {source}" - subprocess.getoutput(cmd) + download(source, dest) if not Path(dest).exists(): Path(dest).touch() @@ -118,7 +115,6 @@ def __init__(self, use_cache: Union[str, bool] = True): data = [] GROUPBY_COLUMNS = ["nro_registro", "tomo", "path"] for keys, group in annotations.groupby(GROUPBY_COLUMNS): - data_ = {} data_["path"] = keys[2] data_["metadata"] = { diff --git a/aymurai/models/peft/__init__.py b/aymurai/models/peft/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/aymurai/models/sentence_encoder/__init__.py b/aymurai/models/sentence_encoder/__init__.py new file mode 100644 index 00000000..c3714e12 --- /dev/null +++ b/aymurai/models/sentence_encoder/__init__.py @@ -0,0 +1,45 @@ +"""Sentence encoder module with sentence-transformers backends. + +Implementations are lazily loaded to avoid importing unavailable dependencies. +Use the factory function `create_encoder()` for automatic backend selection, +or import specific implementations directly from their modules: + + from aymurai.models.sentence_encoder.sentence_transformers_encoder import DistilUSEEncoder +""" + +from aymurai.models.sentence_encoder.base import BaseSentenceEncoder +from aymurai.models.sentence_encoder.factory import ( + EncoderType, + create_encoder, +) + + +def __getattr__(name: str): + """Lazy loading of encoder implementations.""" + if name == "DistilUSEEncoder": + from aymurai.models.sentence_encoder.sentence_transformers_encoder import ( + DistilUSEEncoder, + ) + + return DistilUSEEncoder + + if name == "MultilingualMiniLMEncoder": + from aymurai.models.sentence_encoder.sentence_transformers_encoder import ( + MultilingualMiniLMEncoder, + ) + + return MultilingualMiniLMEncoder + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +__all__ = [ + # Base interface + "BaseSentenceEncoder", + # Factory + "EncoderType", + "create_encoder", + # Implementations (lazy loaded) + "DistilUSEEncoder", + "MultilingualMiniLMEncoder", +] diff --git a/aymurai/models/usem/base.py b/aymurai/models/sentence_encoder/base.py similarity index 100% rename from aymurai/models/usem/base.py rename to aymurai/models/sentence_encoder/base.py diff --git a/aymurai/models/sentence_encoder/factory.py b/aymurai/models/sentence_encoder/factory.py new file mode 100644 index 00000000..a5d5b541 --- /dev/null +++ b/aymurai/models/sentence_encoder/factory.py @@ -0,0 +1,102 @@ +""" +Factory for creating sentence encoder instances based on configuration. +""" + +import os +from enum import Enum +from typing import Optional + +from aymurai.logger import get_logger +from aymurai.models.sentence_encoder.base import BaseSentenceEncoder + +logger = get_logger(__name__) + + +class EncoderType(str, Enum): + """Available encoder implementations.""" + + DISTILUSE = "distiluse" + MINILM = "minilm" + + +def _get_encoder_type_from_env() -> EncoderType: + """Get encoder type from environment variable.""" + env_value = os.getenv("SENTENCE_ENCODER_TYPE", "distiluse").lower() + try: + return EncoderType(env_value) + except ValueError: + logger.warning( + f"Invalid SENTENCE_ENCODER_TYPE '{env_value}', falling back to 'distiluse'" + ) + return EncoderType.DISTILUSE + + +def _coerce_encoder_type(value: EncoderType | str | None) -> EncoderType: + """ + Normalize encoder type values into EncoderType. + + Args: + value: Encoder type as EncoderType, string, or None. If None, reads from env var. + + Returns: + EncoderType: Normalized encoder type. + """ + if value is None: + return _get_encoder_type_from_env() + + if isinstance(value, EncoderType): + return value + + if isinstance(value, str): + try: + return EncoderType(value.lower()) + except ValueError as exc: + raise ValueError(f"Unknown encoder type: {value}") from exc + + raise TypeError(f"Unsupported encoder type value: {type(value)!r}") + + +def create_encoder( + encoder_type: EncoderType | str | None = None, + device: Optional[str] = None, +) -> BaseSentenceEncoder: + """ + Factory function to create the appropriate sentence encoder. + + Args: + encoder_type: Type of encoder to create. Accepts EncoderType or string. + If None, reads from SENTENCE_ENCODER_TYPE env var + (defaults to 'distiluse'). + device: Device for sentence-transformers models. + + Returns: + An instance of BaseSentenceEncoder. + + Environment Variables: + SENTENCE_ENCODER_TYPE: One of 'distiluse', 'minilm'. + - 'distiluse': Use distiluse-base-multilingual-cased-v2 + - 'minilm': Use paraphrase-multilingual-MiniLM-L12-v2 + + Raises: + ImportError: If required dependencies are not available. + ValueError: If an invalid encoder type is specified. + """ + encoder_type = _coerce_encoder_type(encoder_type) + + # Create the appropriate encoder + if encoder_type == EncoderType.DISTILUSE: + from aymurai.models.sentence_encoder.sentence_transformers_encoder import ( + DistilUSEEncoder, + ) + + return DistilUSEEncoder(device=device) + + elif encoder_type == EncoderType.MINILM: + from aymurai.models.sentence_encoder.sentence_transformers_encoder import ( + MultilingualMiniLMEncoder, + ) + + return MultilingualMiniLMEncoder(device=device) + + else: + raise ValueError(f"Unknown encoder type: {encoder_type}") diff --git a/aymurai/models/usem/sentence_transformers_encoder.py b/aymurai/models/sentence_encoder/sentence_transformers_encoder.py similarity index 98% rename from aymurai/models/usem/sentence_transformers_encoder.py rename to aymurai/models/sentence_encoder/sentence_transformers_encoder.py index b7cf81b4..53668f01 100644 --- a/aymurai/models/usem/sentence_transformers_encoder.py +++ b/aymurai/models/sentence_encoder/sentence_transformers_encoder.py @@ -9,7 +9,7 @@ from sentence_transformers import SentenceTransformer from aymurai.logger import get_logger -from aymurai.models.usem.base import BaseSentenceEncoder +from aymurai.models.sentence_encoder.base import BaseSentenceEncoder logger = get_logger(__name__) diff --git a/aymurai/models/usem/__init__.py b/aymurai/models/usem/__init__.py deleted file mode 100644 index ce72ab75..00000000 --- a/aymurai/models/usem/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Sentence encoder module with multiple backend implementations. - -Implementations are lazily loaded to avoid importing unavailable dependencies. -Use the factory function `create_encoder()` for automatic backend selection, -or import specific implementations directly from their modules: - - from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder - from aymurai.models.usem.sentence_transformers_encoder import DistilUSEEncoder -""" - -from aymurai.models.usem.base import BaseSentenceEncoder -from aymurai.models.usem.factory import EncoderType, create_encoder, get_encoder -from aymurai.models.usem.core import SentenceRetrieval - - -def __getattr__(name: str): - """Lazy loading of encoder implementations.""" - if name == "TensorFlowUSEEncoder" or name == "USEMQA": - from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder - return TensorFlowUSEEncoder - - if name == "DistilUSEEncoder": - from aymurai.models.usem.sentence_transformers_encoder import DistilUSEEncoder - return DistilUSEEncoder - - if name == "MultilingualMiniLMEncoder": - from aymurai.models.usem.sentence_transformers_encoder import MultilingualMiniLMEncoder - return MultilingualMiniLMEncoder - - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - - -__all__ = [ - # Base interface - "BaseSentenceEncoder", - # Factory - "EncoderType", - "create_encoder", - "get_encoder", - # Implementations (lazy loaded) - "TensorFlowUSEEncoder", - "DistilUSEEncoder", - "MultilingualMiniLMEncoder", - # Backwards compatibility (lazy loaded) - "USEMQA", - # Retrieval - "SentenceRetrieval", -] diff --git a/aymurai/models/usem/core.py b/aymurai/models/usem/core.py deleted file mode 100644 index 5a3e6e27..00000000 --- a/aymurai/models/usem/core.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Sentence encoder implementations and retrieval utilities.""" - -from typing import TYPE_CHECKING, Optional - -import numpy as np - -from aymurai.models.usem.base import BaseSentenceEncoder -from aymurai.models.usem.factory import EncoderType, create_encoder - -# Type hints only - no runtime import -if TYPE_CHECKING: - from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder - from aymurai.models.usem.sentence_transformers_encoder import ( - DistilUSEEncoder, - MultilingualMiniLMEncoder, - ) - - -class SentenceRetrieval: - """Retrieve similar sentences using sentence embeddings.""" - - def __init__( - self, - categories: list[str], - response_embeddings_path: str, - encoder: Optional[BaseSentenceEncoder] = None, - encoder_type: Optional[EncoderType] = None, - ): - """ - Initialize SentenceRetrieval. - - Args: - categories: List of category labels. - response_embeddings_path: Path to pre-computed embeddings (.npy file). - encoder: Pre-configured encoder instance. If None, creates one using factory. - encoder_type: Type of encoder to create if encoder is None. - If None, reads from SENTENCE_ENCODER_TYPE env var. - """ - self.encoder = encoder if encoder is not None else create_encoder(encoder_type) - self.categories = categories - self.usem_vectors = self.load_usem_vectors(response_embeddings_path) - - # Backwards compatibility alias - self.usem = self.encoder - - def load_usem_vectors(self, file_path): - usem_vectors = np.load(file_path) - return usem_vectors - - def retrieve(self, text: str, top_k: int = 10) -> list[str]: - query_vector = self.usem.encode( - [text], - encoder_type="question_encoder", - ) - - products = np.inner(query_vector, self.usem_vectors)[0] - similar_idx = np.flip(products.argsort())[:top_k] - similar_sentences = [self.categories[idx] for idx in similar_idx] - - return similar_sentences diff --git a/aymurai/models/usem/factory.py b/aymurai/models/usem/factory.py deleted file mode 100644 index 7f3ae49a..00000000 --- a/aymurai/models/usem/factory.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -Factory for creating sentence encoder instances based on configuration. -""" - -import os -import platform -from enum import Enum -from typing import Optional - -from aymurai.logger import get_logger -from aymurai.models.usem.base import BaseSentenceEncoder - -logger = get_logger(__name__) - - -class EncoderType(str, Enum): - """Available encoder implementations.""" - - TENSORFLOW_USE = "tensorflow" - DISTILUSE = "distiluse" - MINILM = "minilm" - AUTO = "auto" - - -def _is_apple_silicon() -> bool: - """Check if running on Apple Silicon.""" - return platform.system() == "Darwin" and platform.machine() == "arm64" - - -def _get_encoder_type_from_env() -> EncoderType: - """Get encoder type from environment variable.""" - env_value = os.getenv("SENTENCE_ENCODER_TYPE", "auto").lower() - try: - return EncoderType(env_value) - except ValueError: - logger.warning( - f"Invalid SENTENCE_ENCODER_TYPE '{env_value}', falling back to 'auto'" - ) - return EncoderType.AUTO - - -def create_encoder( - encoder_type: Optional[EncoderType] = None, - device: Optional[str] = None, -) -> BaseSentenceEncoder: - """ - Factory function to create the appropriate sentence encoder. - - Args: - encoder_type: Type of encoder to create. If None, reads from - SENTENCE_ENCODER_TYPE env var (defaults to 'auto'). - device: Device for sentence-transformers models (ignored for TensorFlow). - - Returns: - An instance of BaseSentenceEncoder. - - Environment Variables: - SENTENCE_ENCODER_TYPE: One of 'tensorflow', 'distiluse', 'minilm', 'auto'. - - 'tensorflow': Use TensorFlow Hub USE-QA (not compatible with Apple Silicon) - - 'distiluse': Use distiluse-base-multilingual-cased-v2 - - 'minilm': Use paraphrase-multilingual-MiniLM-L12-v2 - - 'auto': Auto-detect based on platform (Apple Silicon -> distiluse, else tensorflow) - - Raises: - ImportError: If required dependencies are not available. - ValueError: If an invalid encoder type is specified. - """ - if encoder_type is None: - encoder_type = _get_encoder_type_from_env() - - # Auto-detect based on platform - if encoder_type == EncoderType.AUTO: - if _is_apple_silicon(): - logger.info( - "Apple Silicon detected, using DistilUSE encoder for compatibility" - ) - encoder_type = EncoderType.DISTILUSE - else: - logger.info("Using TensorFlow USE encoder") - encoder_type = EncoderType.TENSORFLOW_USE - - # Create the appropriate encoder - if encoder_type == EncoderType.TENSORFLOW_USE: - from aymurai.models.usem.tensorflow_encoder import TensorFlowUSEEncoder - - return TensorFlowUSEEncoder() - - elif encoder_type == EncoderType.DISTILUSE: - from aymurai.models.usem.sentence_transformers_encoder import DistilUSEEncoder - - return DistilUSEEncoder(device=device) - - elif encoder_type == EncoderType.MINILM: - from aymurai.models.usem.sentence_transformers_encoder import ( - MultilingualMiniLMEncoder, - ) - - return MultilingualMiniLMEncoder(device=device) - - else: - raise ValueError(f"Unknown encoder type: {encoder_type}") - - -# Backwards compatibility alias -def get_encoder(**kwargs) -> BaseSentenceEncoder: - """Alias for create_encoder for backwards compatibility.""" - return create_encoder(**kwargs) diff --git a/aymurai/models/usem/tensorflow_encoder.py b/aymurai/models/usem/tensorflow_encoder.py deleted file mode 100644 index 243e948e..00000000 --- a/aymurai/models/usem/tensorflow_encoder.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -TensorFlow Hub Universal Sentence Encoder implementation. -""" - -from multiprocessing import cpu_count -from typing import Iterable, Optional - -import numpy as np -import tensorflow as tf -import tensorflow_hub as hub -import tensorflow_text # noqa -from more_itertools import chunked -from tqdm.auto import tqdm - -from aymurai.logger import get_logger -from aymurai.models.usem.base import BaseSentenceEncoder - -logger = get_logger(__name__) - -N_JOBS = cpu_count() - - -class TensorFlowUSEEncoder(BaseSentenceEncoder): - """ - TensorFlow Hub Universal Sentence Encoder Multilingual QA implementation. - - This implementation requires tensorflow-text which is not available on Apple Silicon. - Use SentenceTransformersEncoder for macOS ARM64 compatibility. - """ - - def __init__( - self, - usem_qa_url: str = "https://tfhub.dev/google/universal-sentence-encoder-multilingual-qa/3", - ): - self.embed = hub.load(usem_qa_url) - - def encode( - self, - text_array: list[str], - encoder_type: str, - context_array: Optional[list[str]] = None, - ) -> np.ndarray: - input_array = [self.normalize_text(text) for text in text_array] - - if encoder_type == "response_encoder": - encoder_params = { - "input": tf.constant(input_array), - "context": tf.constant(context_array), - } - - if encoder_type == "question_encoder": - encoder_params = { - "input": tf.constant(input_array), - } - - encoded = self.embed.signatures[encoder_type](**encoder_params) - encoded = encoded["outputs"] - - return encoded - - def batch_encode( - self, - text_array: Iterable[str], - encoder_type: str, - batch_size: int = 256, - ) -> np.ndarray: - text_list = list(text_array) - text_chunks = chunked(text_list, batch_size) - - encoded = [ - self.encode(list(text_chunk), encoder_type) - for text_chunk in tqdm( - text_chunks, - desc="creating USEM vectors...", - total=len(text_list) // batch_size, - ) - ] - - encoded = np.vstack(encoded) - - return encoded diff --git a/aymurai/transforms/entity_subcategories/bm25.py b/aymurai/transforms/entity_subcategories/bm25.py new file mode 100644 index 00000000..cb88d145 --- /dev/null +++ b/aymurai/transforms/entity_subcategories/bm25.py @@ -0,0 +1,101 @@ +import re +from typing import Callable + +import numpy as np + + +class BM25Scorer: + """Lightweight BM25 scorer over subcategory strings.""" + + def __init__(self, subcategories: list[str], normalize_fn: Callable[[str], str]): + """ + Initialize the BM25Scorer with subcategories and a normalization function. + + Args: + subcategories (list[str]): List of subcategory strings to be scored. + normalize_fn (Callable[[str], str]): Function to normalize text before tokenization. + """ + self.subcategories = subcategories + self.token_pattern = re.compile(r"\w+", re.UNICODE) + self.normalize_fn = normalize_fn + + tokenized = [self._tokenize(sub) for sub in subcategories] + self.doc_len = [len(toks) for toks in tokenized] + self.avgdl = sum(self.doc_len) / len(self.doc_len) if self.doc_len else 0.0 + self.N = len(tokenized) + self.doc_freqs = [self._to_counter(toks) for toks in tokenized] + corpus_tokens = set().union(*self.doc_freqs) if self.doc_freqs else set() + self.df = { + token: sum(1 for doc in self.doc_freqs if token in doc) + for token in corpus_tokens + } + # Using BM25+ style IDF smoothing + self.idf = { + token: np.log(1 + (self.N - freq + 0.5) / (freq + 0.5)) + for token, freq in self.df.items() + } + + def _tokenize(self, text: str) -> list[str]: + """ + Tokenize the input text after normalization. + + Args: + text (str): Input text to tokenize. + + Returns: + list[str]: List of tokens. + """ + normalized = self.normalize_fn(text) + return self.token_pattern.findall(normalized) + + def _to_counter(self, tokens: list[str]) -> dict[str, int]: + """ + Convert a list of tokens into a frequency counter. + + Args: + tokens (list[str]): List of tokens. + + Returns: + dict[str, int]: Frequency counter of tokens. + """ + counts = {} + + for t in tokens: + counts[t] = counts.get(t, 0) + 1 + + return counts + + def score_vector(self, text: str, k1: float = 1.2, b: float = 0.75) -> np.ndarray: + """ + Compute BM25 scores for the input text against all subcategories. + + Args: + text (str): Input text to score. + k1 (float, optional): BM25 k1 parameter. Defaults to 1.2. + b (float, optional): BM25 b parameter. Defaults to 0.75. + + Returns: + np.ndarray: + """ + scores = np.zeros(self.N, dtype=np.float64) + tokens = self._tokenize(text) + + for token in tokens: + idf = self.idf.get(token) + if idf is None: + continue + + for idx, freq_map in enumerate(self.doc_freqs): + freq = freq_map.get(token, 0) + if freq == 0: + continue + + # BM25 scoring formula + denom = freq + k1 * ( + 1 - b + b * self.doc_len[idx] / (self.avgdl + 1e-9) + ) + + # Update score + scores[idx] += idf * freq * (k1 + 1) / denom + + return scores diff --git a/aymurai/transforms/entity_subcategories/sentence_transformer.py b/aymurai/transforms/entity_subcategories/sentence_transformer.py new file mode 100644 index 00000000..f662d043 --- /dev/null +++ b/aymurai/transforms/entity_subcategories/sentence_transformer.py @@ -0,0 +1,270 @@ +import os +from copy import deepcopy +from pathlib import Path +from typing import Iterable + +import numpy as np + +from aymurai.logger import get_logger +from aymurai.meta.pipeline_interfaces import Transform +from aymurai.meta.types import DataItem +from aymurai.models.sentence_encoder.base import BaseSentenceEncoder +from aymurai.models.sentence_encoder.factory import create_encoder +from aymurai.transforms.entity_subcategories.bm25 import BM25Scorer +from aymurai.transforms.entity_subcategories.subcategories import SUBCATEGORIES +from aymurai.transforms.entity_subcategories.utils import filter_by_category +from aymurai.utils.misc import get_element + +logger = get_logger(__name__) + + +class SentenceTransformerSubcategorizer(Transform): + """ + Hybrid sentence-transformer + optional BM25 subcategorizer. + + - When ``bm25_weight > 0`` mixes BM25 and encoder cosine scores. + - When ``bm25_weight <= 0`` it falls back to encoder-only retrieval. + - Subcategories are normalized (underscore -> space, then encoder.normalize_text) before encoding. + """ + + def __init__( + self, + category: str, + embeddings_path: str, + encoder_name: str = "distiluse", + bm25_weight: float = 0.5, + device: str | None = None, + batch_size: int = 256, + rebuild_embeddings: bool = False, + encoder: BaseSentenceEncoder | None = None, + ): + """ + SentenceTransformerSubcategorizer constructor. + + Args: + category (str): Category to subcategorize. + embeddings_path (str): Path to store/load subcategory embeddings. + encoder_name (str, optional): Name of the encoder to use. Defaults to "distiluse". + bm25_weight (float, optional): Weight for BM25 scoring. Defaults to 0.5. + device (str | None, optional): Device to run the encoder on. Defaults to None. + batch_size (int, optional): Batch size for encoding. Defaults to 256. + rebuild_embeddings (bool, optional): Whether to rebuild embeddings even if cached ones exist. Defaults to False. + encoder (BaseSentenceEncoder | None, optional): Encoder instance to use. Defaults to None. + """ + self.category = category + self.bm25_weight = float(bm25_weight) + self.batch_size = batch_size + + cache_root = Path( + os.getenv("AYMURAI_CACHE_BASEPATH", "/resources/cache/aymurai") + ) + self.cache_path = cache_root / self.__class__.__name__ + self.cache_path.mkdir(parents=True, exist_ok=True) + + self.subcategories = self._load_subcategories(category) + + self.encoder_name = encoder_name + self.encoder = encoder or create_encoder( + encoder_type=encoder_name, device=device + ) + + embeddings_path = Path(embeddings_path) + if not embeddings_path.is_absolute(): + embeddings_path = self.cache_path / embeddings_path + embeddings_path.parent.mkdir(parents=True, exist_ok=True) + self.embeddings_path = embeddings_path + + self.response_vectors = self._load_or_build_embeddings(rebuild_embeddings) + self.bm25 = ( + BM25Scorer(self.subcategories, normalize_fn=self._normalize_subcategory) + if self.bm25_weight > 0 + else None + ) + + def _load_subcategories(self, category: str) -> list[str]: + """ + Load subcategories for a given category. + + Args: + category (str): Category name. + + Raises: + ValueError: If no subcategories are found for the given category. + + Returns: + list[str]: List of subcategories. + """ + key = category.lower().replace(" ", "_") + try: + return SUBCATEGORIES[key] + except KeyError as exc: + raise ValueError( + f"No subcategories found for category '{category}'" + ) from exc + + def _load_or_build_embeddings(self, rebuild: bool) -> np.ndarray: + """ + Load or build subcategory response embeddings. + + Args: + rebuild (bool): Whether to rebuild embeddings even if cached ones exist. + + Raises: + ValueError: If the embeddings file is missing expected data. + + Returns: + np.ndarray: Array of subcategory embeddings. + """ + if self.embeddings_path.exists() and not rebuild: + data = np.load(self.embeddings_path, allow_pickle=True) + if isinstance(data, np.lib.npyio.NpzFile): + vectors = data.get("vectors") + subs = data.get("subcategories") + if vectors is None: + raise ValueError("Embeddings file missing 'vectors'") + if subs is not None and len(subs) != len(self.subcategories): + logger.warning("Subcategory count mismatch; rebuilding embeddings") + else: + return vectors + else: + return data + + logger.info("Building response embeddings for subcategories") + + # Apply the same normalization used in notebooks: replace underscores, then encoder normalize. + normalized = [self._normalize_subcategory(sub) for sub in self.subcategories] + vectors = self.encoder.batch_encode( + normalized, + encoder_type="response_encoder", + batch_size=self.batch_size, + ) + + np.savez( + self.embeddings_path, + vectors=vectors, + subcategories=self.subcategories, + ) + logger.info(f"Saved response embeddings to {self.embeddings_path}") + + return vectors + + def _normalize_subcategory(self, name: str) -> str: + """ + Normalize subcategory strings for stable embeddings. + + Args: + name (str): Subcategory name. + + Returns: + str: Normalized subcategory name. + """ + + name = name.replace("_", " ") + + return self.encoder.normalize_text(name) + + def _combine_scores( + self, sim_scores: np.ndarray, bm25_scores: np.ndarray + ) -> np.ndarray: + """ + Combine similarity and BM25 scores. + + Args: + sim_scores (np.ndarray): Similarity scores. + bm25_scores (np.ndarray): BM25 scores. + + Returns: + np.ndarray: Combined scores. + """ + if self.bm25_weight <= 0: + return sim_scores + + bm25_max = bm25_scores.max() if bm25_scores.size else 0.0 + sim_max = sim_scores.max() if sim_scores.size else 0.0 + + bm25_norm = ( + bm25_scores / (bm25_max + 1e-9) + if bm25_max > 0 + else np.zeros_like(bm25_scores) + ) + + sim_norm = ( + sim_scores / (sim_max + 1e-9) if sim_max > 0 else np.zeros_like(sim_scores) + ) + + return self.bm25_weight * bm25_norm + (1 - self.bm25_weight) * sim_norm + + def retrieve(self, text: str, top_k: int = 10) -> list[str]: + """ + Retrieve top-k subcategories for a given text. + + Args: + text (str): Input text. + top_k (int, optional): Number of top subcategories to retrieve. Defaults to 10. + + Returns: + list[str]: List of top-k subcategories. + """ + return self.batch_retrieve([text], top_k=top_k)[0] + + def batch_retrieve(self, texts: Iterable[str], top_k: int = 10) -> list[list[str]]: + """ + Batch retrieve top-k subcategories for given texts. + + Args: + texts (Iterable[str]): Input texts. + top_k (int, optional): Number of top subcategories to retrieve. Defaults to 10. + + Returns: + list[list[str]]: List of lists of top-k subcategories for each input text. + """ + texts = list(texts) + if not texts: + return [] + + query_vectors = self.encoder.batch_encode( + texts, encoder_type="question_encoder", batch_size=self.batch_size + ) + similarity = np.inner(query_vectors, self.response_vectors) + k = min(top_k, similarity.shape[1]) + + results = [] + for idx, text in enumerate(texts): + sim_scores = similarity[idx] + + if self.bm25 and self.bm25_weight > 0: + bm25_scores = self.bm25.score_vector(text) + combined = self._combine_scores(sim_scores, bm25_scores) + else: + combined = sim_scores + + top_indices = np.argsort(-combined)[:k] + results.append([self.subcategories[i] for i in top_indices]) + + return results + + def __call__(self, item: DataItem) -> DataItem: + """ + Apply subcategorization to entities in the data item. + + Args: + item (DataItem): Input data item. + + Returns: + DataItem: Data item with added subclass labels. + """ + item = deepcopy(item) + ents = get_element(item, levels=["predictions", "entities"]) or [] + texts = [ent["text"] for ent in ents] + filtered_ents = filter_by_category(ents, self.category) + retrieved = self.batch_retrieve(texts, top_k=5) + + for ent, retrieved_ in zip(ents, retrieved): + if ent in filtered_ents and not get_element( + ent, levels=["attrs", "aymurai_label_subclass"] + ): + ent.setdefault("attrs", {})["aymurai_label_subclass"] = retrieved_ + + item.setdefault("predictions", {})["entities"] = ents + + return item diff --git a/aymurai/transforms/entity_subcategories/subcategories.py b/aymurai/transforms/entity_subcategories/subcategories.py new file mode 100644 index 00000000..ad4fd5a9 --- /dev/null +++ b/aymurai/transforms/entity_subcategories/subcategories.py @@ -0,0 +1,638 @@ +SUBCATEGORIES = { + "conducta": [ + "abandonar_animal_domestico", + "abandono_de_personas", + "abuso_de_armas", + "abuso_de_autoridad_e_incumplimiento_deberes_funcionario_publico", + "abuso_sexual", + "acceder_a_lugares_distintos_segun_entrada", + "acceder_lugares_distintos_segun_entrada", + "acceso_a_sistema_restringido", + "acoso_sexual_callejero", + "actividades_lucrativas_sin_autorizacion", + "actividades_lucrativas_sin_habilitacion", + "actos_contenido_sexual_con_menores", + "administracion_fraudulenta", + "afectar_desarrollo_espectaculo", + "afectar_el_desarrollo_del_espectaculo", + "afectar_funcionamiento_servicios_publicos", + "afectar_servicios_de_emergencia_o_seguridad", + "afectar_servicios_emergencia", + "afectar_señalizacion", + "allanamiento_autonomo", + "alterar_programa", + "alterar_sepulturas", + "amenazas", + "amparo", + "apariencia_falsa", + "apariencia_falsa_para_entrar_a_domicilio_o_lugar_privado", + "apropiacion_indebida_de_tributos", + "arrojar_cosas_que_puedan_causar_lesiones", + "arrojar_cosas_sustancias", + "arrojar_sustancias_insalubres_en_lugares_publicos", + "asociacion_ilicita", + "asuncion_falsa_de_contravencion", + "atentado_contra_la_autoridad", + "ausencia_de_habilitacion", + "banderas", + "carteles_afiches_volantes", + "cierre_defectuoso", + "circular_por_carriles_exclusivos", + "circular_por_lugar_prohibido", + "cohecho_activo", + "cohecho_pasivo", + "coimas", + "competencia_desleal", + "conducir_con_mayor_cantidad_de_alcohol_en_sangre_del_permitido", + "conducir_con_mayor_grado_de_alcohol_o_bajo_efectos_de_estupefacientes", + "conducir_sin_licencia_que_lo_habilite_por_categoria_de_vehiculo", + "conducir_usando_aparato_electronico", + "connivencia_policial", + "contactar_menor_por_medio_de_tecnologias_para_cometer_delitos_contra_su_integridad_sexual", + "cruce_de_semaforo_en_rojo", + "cuida_coche", + "cuidar_coches_sin_autorizacion", + "daños", + "daños_informaticos", + "defraudacion", + "defraudacion_contra_la_administracion_publica", + "delito_contra_seguridad_transito", + "denegacion_de_justicia", + "derecho_admision", + "desarmado_automotor", + "desinfeccion_y_desratizacion", + "desobediencia_a_cargas_procesales", + "desobediencia_a_la_autoridad", + "difusion_no_autorizada_de_imagenes", + "difusion_no_autorizada_de_imagenes_intimas", + "discriminar", + "documentacion_sanitaria", + "ejecucion_de_multa", + "ejercer_ilegitimamente_actividad", + "ejercicio_ilegal_de_la_medicina", + "elementos_de_prevencion_contra_incendio", + "encubrimiento", + "encubrimiento_actividades_baile", + "ensuciar_bienes", + "entregar_indebidamente_armas_explosivos", + "espantar_animales", + "espejos_retrovisores", + "estacionamiento_prohibido", + "estacionamiento_sobre_senda_peatonal", + "estafa", + "estupefacientes", + "exceso_capacidad_ingreso", + "exceso_de_velocidad", + "exhibicion_de_documentacion_obligatoria", + "exhibiciones_obscenas", + "exhortos_de_otras_jurisdicciones", + "explotacion_trabajo_infantil", + "extorsion", + "fabricar_transportar_artefactos_pirotecnicos", + "falsa_denuncia", + "falsedad_documental_de_certificado_de_aptitud_ambiental", + "falsificacion_certificado_medico", + "falsificacion_de_documento", + "falsificacion_de_marcas_firmas_señas_oficiales", + "falsificacion_de_numeracion_de_objeto_registrada_de_acuerdo_a_ley", + "falta_de_poliza_de_seguros", + "favorecimiento_evasion_persona_detenida", + "frustrar_subasta_publica", + "guardar_artefactos_pirotecnicos", + "guardar_elementos_aptos_violencia", + "habeas_corpus", + "habilitacion_en_infraccion", + "homicidio", + "hostigamiento", + "hostigamiento_digital", + "hurto", + "impedimento_de_contacto_de_menor_con_padre_no_conviviente", + "incendio_explosion_inundacion_con_peligro_para_bienes", + "incendios_y_otros_estragos", + "incitar_al_desorden", + "incitar_desorden", + "incumplimiento_de_plazos", + "incumplimiento_deberes_familiares", + "incumplimiento_deberes_funcionario_publico", + "incumplimiento_medidas_prevencion_sanidad", + "incumplimiento_perimetro_profundidad", + "incumplir_clausura", + "incumplir_obligaciones_legales", + "inducir_menor_a_mendigar", + "infraccion_reglamentos_seguridad", + "ingresar_artefactos_pirotecnicos", + "ingresar_consumir_bebidas_alcoholicas", + "ingresar_contra_derecho_admision", + "ingresar_sin_autorizacion_lugares_reservados", + "ingresar_sin_entrada", + "ingresar_sin_entrada_autorizacion", + "ingreso_a_domicilio_sin_autorizacion", + "inhumar_exhumar_profanar", + "intimidacion", + "juego_sin_autorizacion", + "juegos_de_azar_sin_autorizacion", + "lesiones", + "ley_451", + "licencia_vencida", + "maltrato", + "mantener_animal_domestico_espacios_inadecuados", + "mantenimiento_de_cercas_y_aceras", + "menoscabar_integridad_animal_domestico", + "mesnna_masnna", + "no_realizar_grabado_de_autopartes", + "no_respetar_carriles", + "no_respetar_senda_peatonal", + "obligacion_de_exhibir_cartel", + "obligacion_de_informar_prohibicion_de_fumar", + "obligacion_de_conservar", + "obstaculizar_ingreso_o_salida", + "obstruccion_de_inspeccion", + "obstruccion_de_via", + "obstruccion_de_via_publica", + "obstruccion_via_publica", + "obstruir_salida", + "ocupar_via_publica", + "oferta_demanda_sexo_espacio_publico", + "omitir_cuidados_animal_domestico", + "omitir_recaudos_de_cuidado_animal_domestico", + "omitir_recaudos_de_organizacion", + "omitir_recaudos_espectaculo_masivo", + "omitir_recaudos_organizacion_seguridad", + "organizar_explotar_juego", + "organizar_sin_autorizacion_juego", + "participar_competencias_velocidad_via_publica", + "pelear_en_lugar_publico", + "permiso_y_planos_de_obra", + "persona_no_habilitada", + "perturbar_ceremonias_religiosas_funebres", + "perturbar_filas_ingreso_no_respetar_vallado", + "placas_de_dominio", + "pornografia_infantil", + "portacion_de_arma", + "portar_armas_no_convencionales", + "presencia_menores_lugar_no_autorizado", + "presunta_comision_delito", + "presunta_contravencion", + "privacion_ilegitima_de_la_libertad", + "producir_avalanchas", + "promocion_o_facilitacion_de_prostitucion_de_personas", + "promover_comerciar_ofertar_juego", + "propaganda_discriminatoria", + "proteccion_animal", + "provocar_parcialidad", + "publicidad_en_lugares_no_habilitados_via_publica", + "regentear_o_administrar_casas_de_tolerancia", + "residuos_peligrosos", + "retencion_indebida", + "revender_entradas", + "robo", + "ruidos_molestos", + "ruidos_y_vibraciones", + "sancion_generica", + "servicios_de_seguridad_prohibiciones_incumplidas", + "servicios_de_seguridad_requisitos_incumplidos", + "suministrar_alcohol_menor", + "suministrar_bebidas_alcoholicas", + "suministrar_elementos_aptos_agresion", + "suministrar_material_pornografico", + "suministrar_objetos_peligrosos", + "suministrar_productos_farmaceuticos", + "suministro_de_medicamentos", + "suplantacion_identidad", + "suplantacion_digital_de_identidad", + "taxis_transportes_escolares_remises_sin_autorizacion", + "tenencia_de_arma", + "transporte_de_pasajeros", + "transporte_de_pasajeros_sin_habilitacion", + "transporte_de_pasajeros_sin_habilitacion_o_autorizacion", + "usar_indebidamente_credencial", + "usar_indebidamente_espacio_publico", + "usar_indebidamente_armas", + "uso_de_documento_falso_o_adulterado", + "uso_indebido_del_espacio_publico", + "uso_o_exhibicion_de_franquicias", + "usurpacion", + "vehiculo_abandonado", + "vender_entradas_o_permitir_ingreso_exceso", + "vender_sustancias_medicinales_sin_receta", + "venta_o_consumo_de_bebidas_alcoholicas_fuera_del_horario", + "verificacion_tecnica", + "violacion_de_peaje", + "violacion_de_domicilio", + "violacion_de_secretos_y_de_la_privacidad", + "violar_clausura", + "violar_inhabilitacion_para_conducir", + "violar_reglamentacion_juego", + "zanjas_y_pozos_en_via_publica", + ], + "conducta_descripcion": [ + "agravada", + "agravadas_por_edad", + "agravadas_por_el_uso_de_armas", + "agravadas_por_uso_de_armas", + "agravadas_por_uso_de_armas_impropia", + "agravado", + "agravado_abuso_funcion", + "agravado_alevosia", + "agravado_causar_sufrimiento_otra_persona", + "agravado_concurso", + "agravado_familiar", + "agravado_insertar_datos_en_datos_personales", + "agravado_lugar_publico", + "agravado_mas_personas", + "agravado_medio_idoneo", + "agravado_menor_de_edad", + "agravado_miembro_fuerzas", + "agravado_monumentos", + "agravado_otro_delito", + "agravado_peligro_de_muerte", + "agravado_por_el_vinculo", + "agravado_por_espacio", + "agravado_por_ser_fuerza_de_seguridad", + "agravado_precio", + "agravado_relacion_de_pareja", + "agravado_superior_militar", + "agravado_violar_informacion_banco_de_datos", + "agravado_violar_sistemas_seguridad", + "agravado_violencia_de_genero", + "agravado_violencia_mujer", + "agravados", + "coactivas", + "coactivas_agravadas", + "coactivas_agravadas_por_uso_de_armas", + "comercio_de_plantas_para_producir_estupefacientes", + "con_escalamiento", + "conduccion_imprudente", + "culposas", + "culposo", + "de_fuego_uso_civil", + "de_guerra", + "destinado_a_acreditar_identidad_de_personas_habilitacion_o_titularidad", + "digital", + "digital_agravado_familiar", + "digital_agravado_jefe", + "digital_agravado_mas_de_una_persona", + "digital_agravado_menor_de_edad", + "digital_agravado_relacion_de_pareja", + "durante_espectaculo_deportivo", + "en_grandes_parques_o_espectaculos_masivos", + "en_riña", + "entrega_suministro_facilitacion", + "funcionario_publico", + "ganzua_llave", + "graves", + "graves_tentativa", + "informatica", + "ingreso_a_domicilio_sin_autorizacion", + "intimadacion_o_invocando_orden_superior", + "leves", + "leves_agravadas", + "muebles_transportables", + "no_denuncia_delito", + "ocasion_de_incendio", + "organismo_publico", + "para_produccion_o_tenencia_con_fines_de_comercializacion", + "por_despojo", + "por_obligacion_de_devolver", + "por_turbacion_de_la_posesion", + "productos_separados_suelo", + "publico_o_privado", + "seguido_de_muerte", + "simple", + "simples", + "sin_autorizacion", + "tarjeta_credito_debito", + "tenencia", + "tentativa", + "tentativa_agravado_alevosia", + "transporte_de_pasajeros_sin_habilitacion_o_autorizacion", + "uso_de_tarjeta_o_datos", + "vehiculos", + ], + "detalle": [ + "audiencia_victima_ley26485", + "30_dias", + "45_dias", + "60_dias", + "90_dias", + "180_dias", + "abandono_vehiculo", + "absolucion", + "absolucion_desistimiento_fiscal", + "accesorias_legales", + "acepta_competencia_inhibitoria", + "acepta_cuestion_turno", + "aclaratoria", + "acta_de_comprobacion", + "acta_de_intimacion", + "acuerdo_de_pago_levanta_embargo", + "acumulacion_por_conexidad_y_ampliacion", + "admisibilidad", + "agente_encubierto", + "agente_revelador", + "agotamiento_de_la_pena", + "alimentos_provisorios", + "allanamiento", + "apelacion", + "apertura_extraccion_examen_datos", + "aprehension", + "arma", + "armas", + "arresto_domiciliario_deja_sin_efecto", + "asesor_tutelar", + "atipicidad_sobreseimiento", + "audiencia_acusado_ley26485", + "audiencia_acusado_difiere_sentencia", + "audiencia_preliminar", + "audiencia_victima_11bis_ley24660", + "bloqueo_acceso_al_dominio", + "cadena_de_custodia_de_elementos_secuestrados", + "camara_gesell", + "cambio_domicilio_prision_domiciliaria", + "captura", + "captura_deja_sin_efecto", + "caucion_real", + "cedula_de_notificacion", + "cese_de_actos_de_perturbacion_o_intimidacion", + "cese_de_medidas", + "cese_de_medidas_parcial", + "cierre_de_instancia", + "clausura", + "clausura_deja_sin_efecto", + "competencia_penal_juvenil", + "concede", + "concede_parcialmente", + "conocimiento_personal", + "consigna_policial", + "contienda_negativa", + "control", + "convalida_fallecimiento", + "convalida_inimputabilidad", + "convalidacion_otras_causales", + "copia_forense_celular", + "copias_de_capturas_de_pantalla_mensajes_y_llamados", + "cosa_juzgada_sobreseimiento", + "cuarto_intermedio", + "cuarto_intermedio_para_resolver", + "cumplimiento_de_regla_de_conducta_en_complejo_penitenciario", + "cumplimiento_de_reglas_de_conducta", + "declaracion_testimonial", + "declina_competencia", + "defensa", + "defensa_particular", + "defensa_querella", + "deja_sin_efecto_inhabilitacion_y_devuelve_licencia", + "deja_sin_efecto_revocacion", + "delitos_de_accion_publica", + "desistimiento_accion_instancia_privada_sobreseimiento", + "desistimiento_fiscal", + "destruccion_de_arma", + "detencion", + "detencion_captura_y/o_traslado", + "detencion_captura_y/o_traslado_deja_sin_efecto", + "detencion_secuestro_y_requisa", + "devolucion_acarreo_y_estadia", + "dictamen_fiscal", + "difiere_decision", + "difiere_fundamentos", + "difiere_resolucion", + "dispositivo_de_geoposicionamiento_mantiene_hasta_juicio", + "domiciliaria_con_vigilancia_electronica", + "egreso_del_pais", + "ejecucion_de_honorarios", + "embargo", + "embargo_deja_sin_efecto", + "estimulo_educativo", + "estupefacientes", + "estupefacientes_dinero", + "estupefacientes_telefono_dinero", + "estupefacientes_telefono_dinero_arma", + "estupefacientes_telefono_vehiculo", + "estupefacientes_vehiculo_dinero", + "excarcelacion", + "exclusion_del_hogar", + "exhorto", + "exhorto_diplomatico", + "exime_pago_multa", + "exime_reparacion_del_daño_y_pago_multa", + "eximicion_pago", + "eximicion_pauta_de_conducta", + "expulsion_art_64_a_ley_25871", + "extincion_accion_por_autocomposicion", + "extincion_accion_por_cumplimiento", + "extincion_accion_sobreseimiento", + "extincion_accion_sobreseimiento_por_fallecimiento", + "extincion_de_la_pena", + "extincion_sancion_por_cumplimiento", + "extrañamiento", + "falta_de_accion_sobreseimiento", + "falta_de_legitimacion_pasiva", + "falta_de_participacion", + "falta_de_presentacion", + "falta_de_requisitos_procedencia", + "fija_nueva_audiencia", + "fondo_de_reserva", + "habeas_corpus", + "hasta_agotar_investigacion", + "identidad_objeto_y_sujeto", + "imagenes_videos", + "incompetencia_material", + "incompetencia_por_conexidad", + "incompetencia_por_conexidad_subjetiva", + "incompetencia_territorial", + "indeterminacion_imputacion", + "informacion_y_apertura_de_antenas_llamadas", + "informe_cij", + "informe_mercado_libre", + "informe_ncmec", + "informe_previo", + "informes_llamadas_y_deteccion_de_celdas", + "inhabilitacion_para_conducir", + "inhabilitacion_para_conducir_deja_sin_efecto", + "inmovilizacion", + "interes_en_el_caso", + "internacion_involuntaria", + "interprete", + "interprete_y_defensa", + "interrogatorio_policial", + "intervencion_equipo_especializado", + "intervencion_previa", + "intervencion_red_social", + "intervencion_telefonica", + "intervencion_telefonica_con_ubicacion_de_celda_de_apertura", + "levantamiento_clausura_administrativa", + "levantamiento_secreto_bancario", + "levantamiento_secreto_fiscal", + "liberacion_aves", + "libertad_asistida_incorporacion", + "libertad_condicional_otorga", + "libertad_condicional_revoca", + "litispendencia", + "mantiene", + "mediacion", + "medidas_restrictivas", + "modifica_pauta_de_conducta", + "morigeracion_domiciliaria", + "no_convalida", + "no_homologa_absolucion", + "notificacion_via_mail", + "nulidad_incorporacion_prueba", + "otorga_plazo", + "otras_vias_idoneas", + "pago_de_multa_en_cuotas", + "pago_minimo_multa_sobreseimiento", + "pago_minimo_multa_y_reparacion_daño_64cp", + "paradero", + "paradero_deja_sin_efecto", + "parcial_requerimiento_de_juicio", + "parcial_requerimiento_de_juicio_falta_de_circunscripcion_temporal", + "parcial_requerimiento_de_juicio_falta_de_fundamentacion", + "parcial_requerimiento_de_juicio_por_introduccion_de_agravante", + "pedido_de_informe_banco", + "pedido_de_informe_facebook", + "pedido_de_informe_facebook_google", + "pedido_de_informe_facebook_microsoft", + "pedido_de_informe_google", + "pedido_de_informe_google_microsoft", + "pedido_de_informe_imgur_llc", + "pedido_de_informe_instagram", + "pedido_de_informe_microsoft", + "pedido_de_informe_skype", + "pedido_de_informe_telefonia", + "pedido_de_informe_whatsapp", + "pedido_informe_tiktok", + "pedido_prision_domiciliaria", + "pena_de_arresto_efectivo_cumplimiento", + "pena_de_arresto_efectivo_cumplimiento_compurgada", + "pena_de_arresto_en_suspenso", + "pena_de_inhabilitacion_especial", + "pena_de_multa_efectivo_cumplimiento", + "pena_de_multa_en_suspenso", + "pena_de_multa_sustituida_por_amonestacion", + "pena_de_multa_sustituida_por_clausura_se_tiene_por_cumplida", + "pena_de_multa_sustituida_por_trabajos_comunitarios", + "pena_de_prision_domiciliaria", + "pena_de_prision_efectivo_cumplimiento", + "pena_de_prision_efectivo_cumplimiento_compurgada", + "pena_de_prision_efectivo_cumplimiento_domiciliaria", + "pena_de_prision_en_suspenso", + "pena_de_tareas_comunitarias", + "pena_de_tareas_de_utilidad_publica", + "pericia", + "pericia_arma", + "pericia_examen_veterinario", + "peritaje_de_telefono", + "peritaje_ordenadores_y_o_telefonos", + "peritaje_telefono", + "perito", + "permiso_de_viaje", + "plazo_razonable_sobreseimiento", + "plazo_razonable_sobresimiento", + "pleito_con_una_parte", + "por_quince_dias_hasta_tomar_contacto_con_victima", + "prescripcion_sobreseimiento", + "prision_domiciliaria_mantiene", + "prision_domiciliaria_prorroga", + "procedimiento", + "procedimiento_administrativo_pendiente", + "prohibicion_compra_tenencia_registracion_de_armas", + "prohibicion_de_acercamiento", + "prohibicion_de_acercamiento_y_contacto", + "prohibicion_de_publicar_en_redes", + "prohibicion_de_publicar_en_redes_sobre", + "promocion_a_periodo_de_prueba_art_7_ley_24660", + "prorroga", + "prueba_ofrecida", + "publicacion_de_edictos", + "queja", + "querella", + "quita_puntos_de_licencia_de_conducir", + "realizacion", + "rebeldia", + "rebeldia_deja_sin_efecto", + "rebeldia_y_captura", + "rebeldia_y_paradero", + "rechaza_acuerdo", + "rechaza_competencia", + "regimen_de_comunicacion_provisoria", + "reincidencia", + "reintegra_puntos_de_licencia_de_conducir", + "remate_vehiculo", + "remision_a_justicia_civil", + "remision_camara_para_sorteo", + "reparacion_integral_del_daño_sobreseimiento", + "replica_de_arma_de_plastico", + "reposicion", + "reposicion_apelacion_subsidio", + "reprogramacion", + "requerido_por_infractor", + "requerimiento_de_juicio", + "requerimiento_de_juicio_falta_de_fundamentacion", + "requisa", + "restitucion_de_inmueble", + "revision_psicofisica_capacidad", + "revision_psiquiatrica_y_psicologica", + "revoca", + "revoca_condicionalidad", + "revocacion", + "rueda_de_reconocimiento", + "salidas_extraordinarias", + "salidas_transitorias", + "sancion_sustitutiva_prorroga", + "secuestro", + "sin_indicacion_plazo", + "sobreseimiento", + "solicita", + "solicitud_destruccion_de_estupefacientes", + "tareas_de_investigacion", + "telefono", + "temor_parcialidad_generico", + "tiene_por_desistido", + "tiene_por_no_computado_plazo", + "tiene_presente", + "tiene_presente_acuerdo", + "tobillera_refractaria", + "traslado_por_fuerza_publica", + "unifica_solicita_inhibitoria", + "unificacion_de_pena", + "vehiculo", + "vencimiento_plazo_ipp", + ], + "objeto_de_la_resolucion": [ + "acumulacion_de_caso", + "admisibilidad_amparo", + "admisibilidad_habeas_corpus", + "admisibilidad_prueba", + "apartamiento", + "archivo_fiscal", + "audiencia_de_conciliacion", + "beneficio_litigar_sin_gastos", + "caducidad_de_instancia", + "cuestion_de_competencia", + "decomiso", + "desistimiento_faltas", + "ejecucion_de_la_pena", + "excepcion", + "excusacion", + "extraccion_testimonios", + "juicio_abreviado", + "juicio_oral", + "juzgamiento_faltas", + "mediacion", + "medida_cautelar", + "medida_probatoria", + "mesa_de_dialogo", + "nulidad", + "prision_preventiva", + "prorroga_investigacion_penal_preparatoria", + "recurso", + "recusacion", + "regulacion_de_honorarios", + "remision_controladora", + "restablecimiento_de_contacto", + "restitucion_de_efectos", + "restitucion_de_inmueble", + "restitucion_efectos", + "suspension_del_proceso_a_prueba", + ], +} diff --git a/aymurai/transforms/entity_subcategories/usem.py b/aymurai/transforms/entity_subcategories/usem.py deleted file mode 100644 index 25645aa6..00000000 --- a/aymurai/transforms/entity_subcategories/usem.py +++ /dev/null @@ -1,121 +0,0 @@ -import os -from hashlib import md5 -from copy import deepcopy - -import numpy as np - -from aymurai.logger import get_logger -from aymurai.meta.types import DataItem -from aymurai.models.usem import create_encoder -from aymurai.utils.download import download -from aymurai.utils.misc import is_url, get_element -from aymurai.meta.pipeline_interfaces import Transform - -from .utils import filter_by_category - -logger = get_logger(__name__) - - -class USEMSubcategorizer(Transform): - """ - Use USEM to retrieve subcategories - """ - - usem = create_encoder() - - def __init__( - self, - category: str, - subcategories_path: list[str], - response_embeddings_path: str, - device: str = "/cpu:0", - ): - """ - Sentence Similarity using USEM (Universal Sentence Encoder Multilingual QA) - - Args: - category (str): category to filter - subcategories_path (list[str]): path to subcategories - response_embeddings_path (str): path to response embeddings - device (str, optional): device to use. Defaults to "/cpu:0". - """ - self.category = category - - CACHE_PATH = os.path.join( - os.getenv("AYMURAI_CACHE_BASEPATH", "/resources/cache/aymurai"), - self.__name__, - ) - - logger.info(f"load usem options from {subcategories_path}") - if is_url(url := subcategories_path): - fname = md5(url.encode("utf-8")).hexdigest() - subcategories_path = f"{CACHE_PATH}/{fname}" - logger.info(f"downloading options on {subcategories_path}") - os.makedirs(CACHE_PATH, exist_ok=True) - subcategories_path = download(url, output=subcategories_path) - - with open(subcategories_path, "r") as file: - self.subcategories = file.read().splitlines() - logger.info(f"options head: {self.subcategories[:5]}") - - # download embeddings - if is_url(url := response_embeddings_path): - fname = md5(url.encode("utf-8")).hexdigest() - model_path = f"{CACHE_PATH}/{fname}" - logger.info(f"downloading embeddings on {model_path}") - os.makedirs(CACHE_PATH, exist_ok=True) - response_embeddings_path = download(url, output=model_path) - logger.info(f"load usem embeddings from {response_embeddings_path}") - self.usem_vectors = self.load_usem_vectors(response_embeddings_path) - self.device = device - - def load_usem_vectors(self, file_path): - """ - Load USEM vectors - """ - usem_vectors = np.load(file_path) - return usem_vectors.copy() - - def retrieve(self, text: str, top_k: int = 10) -> list[str]: - """ - Retrieve similar sentences using USEM - """ - query_vector = self.usem.encode( - [text], - encoder_type="question_encoder", - ) - - products = np.inner(query_vector, self.usem_vectors)[0] - similar_idx = np.flip(products.argsort())[:top_k] - similar_sentences = [self.subcategories[idx] for idx in similar_idx] - - return similar_sentences - - def __call__(self, item: DataItem) -> DataItem: - """ - Retrieve subcategories for entities. If the entity is not in the category of interest, - the subcategory is not retrieved. - - Args: - item (DataItem): input data item - - Returns: - DataItem: output data item - """ - item = deepcopy(item) - - ents = get_element(item, levels=["predictions", "entities"]) or [] - - texts = list(map(lambda x: x["text"], ents)) - filtered_ents = filter_by_category(ents, self.category) - retrieved = list(map(self.retrieve, texts)) - - for ent, retrieved_ in zip(ents, retrieved): - if ent in filtered_ents and not get_element( - ent, levels=["attrs", "aymurai_label_subclass"] - ): - ent["attrs"]["aymurai_label_subclass"] = retrieved_ - - item["predictions"]["entities"] = ents - - return item diff --git a/aymurai/utils/download.py b/aymurai/utils/download.py index 17151f32..9ce6ea6a 100644 --- a/aymurai/utils/download.py +++ b/aymurai/utils/download.py @@ -1,6 +1,9 @@ import os +import shutil +import tempfile +from pathlib import Path -import gdown +import requests from aymurai.logger import get_logger @@ -9,30 +12,36 @@ logger = get_logger(__name__) -def download(url, output): +def download(url: str, output: str) -> str: """ - Download file from url - skip if file exists and environment variable NO_DOWNLOAD_IF_EXISTS is set to True + Stream download to a file. + + Skips download when the target exists and NO_DOWNLOAD_IF_EXISTS is truthy. Args: - url (str): url to download - output (str): output path + url: URL to download. + output: Path to save the downloaded file. Returns: - str: output path + str: Path to the downloaded file. """ - if os.path.exists(output) and bool(NO_DOWNLOAD_IF_EXISTS): - logger.warn( + output_path = Path(output) + output_path.parent.mkdir(parents=True, exist_ok=True) + + if output_path.exists() and NO_DOWNLOAD_IF_EXISTS: + logger.warning( "File found and skipping. Set NO_DOWNLOAD_IF_EXISTS environment to false to force download." ) - return output - - gdown.download( - url, - quiet=False, - fuzzy=True, - resume=True, - output=output, - ) - return output + return str(output_path) + + logger.info(f"Downloading {url} -> {output_path}") + with requests.get(url, stream=True, allow_redirects=True, timeout=60) as resp: + resp.raise_for_status() + with tempfile.NamedTemporaryFile(delete=False) as tmp: + for chunk in resp.iter_content(chunk_size=8192): + if chunk: + tmp.write(chunk) + tmp_path = Path(tmp.name) + shutil.move(tmp_path, output_path) + return str(output_path) diff --git a/notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb b/notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb new file mode 100755 index 00000000..d43dae54 --- /dev/null +++ b/notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb @@ -0,0 +1,2366 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2a8e7308", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "%load_ext rich" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "730c04d9", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "import random\n", + "from collections import defaultdict\n", + "from copy import deepcopy\n", + "from pathlib import Path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "from IPython.display import Markdown\n", + "from rich import print\n", + "\n", + "from aymurai.utils.json_data import load_json\n", + "\n", + "sns.set_theme(style=\"whitegrid\")\n", + "plt.rcParams.update(\n", + " {\"figure.figsize\": (10, 6), \"axes.titlesize\": 14, \"axes.labelsize\": 12}\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "211f50a2", + "metadata": {}, + "source": [ + "## Load annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1f76f76", + "metadata": {}, + "outputs": [], + "source": [ + "annotations_path = \"/resources/annotations/label-studio/resos-annotations/30-nov/project-3-at-2022-11-30-16-04-2b43bf39.json\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0e13682", + "metadata": {}, + "outputs": [], + "source": [ + "annotations = load_json(annotations_path)\n", + "print(f\"Loaded {len(annotations)} annotations\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5080fdc6", + "metadata": {}, + "outputs": [], + "source": [ + "def collect_label_annotations(annotations, target_labels):\n", + " \"\"\"Build minimal evaluation buckets keyed by label.\"\"\"\n", + " buckets_by_label = defaultdict(list)\n", + " target_labels = set(target_labels)\n", + "\n", + " for task in annotations:\n", + " for annotation in task.get(\"annotations\", []):\n", + " merged = {}\n", + " for result in annotation.get(\"result\", []):\n", + " result_id = result.get(\"id\")\n", + " value = result.get(\"value\", {})\n", + " slot = merged.setdefault(\n", + " result_id,\n", + " {\n", + " \"text\": value.get(\"text\"),\n", + " \"start\": value.get(\"start\"),\n", + " \"end\": value.get(\"end\"),\n", + " \"labels\": [],\n", + " \"choices\": [],\n", + " },\n", + " )\n", + "\n", + " # Update span info if present in the current result.\n", + " slot[\"text\"] = slot[\"text\"] or value.get(\"text\")\n", + " slot[\"start\"] = (\n", + " slot[\"start\"]\n", + " if slot[\"start\"] is not None\n", + " else value.get(\"start\")\n", + " )\n", + " slot[\"end\"] = (\n", + " slot[\"end\"]\n", + " if slot[\"end\"] is not None\n", + " else value.get(\"end\")\n", + " )\n", + " slot[\"labels\"].extend(value.get(\"labels\", []))\n", + " slot[\"choices\"].extend(value.get(\"choices\", []))\n", + "\n", + " for payload in merged.values():\n", + " labels = [\n", + " label\n", + " for label in payload[\"labels\"]\n", + " if label in target_labels\n", + " ]\n", + " if not labels or payload[\"text\"] is None:\n", + " continue\n", + "\n", + " item = {\n", + " \"text\": payload[\"text\"],\n", + " \"labels\": list(dict.fromkeys(payload[\"labels\"])),\n", + " \"choices\": list(dict.fromkeys(payload[\"choices\"])),\n", + " \"start\": payload[\"start\"],\n", + " \"end\": payload[\"end\"],\n", + " }\n", + "\n", + " for label in labels:\n", + " buckets_by_label[label].append(deepcopy(item))\n", + "\n", + " return {label: buckets_by_label.get(label, []) for label in target_labels}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4e9d0ab", + "metadata": {}, + "outputs": [], + "source": [ + "target_labels = [\n", + " \"CONDUCTA\",\n", + " \"CONDUCTA_DESCRIPCION\",\n", + " \"DETALLE\",\n", + " \"OBJETO_DE_LA_RESOLUCION\",\n", + "]\n", + "samples_by_label = collect_label_annotations(annotations, target_labels)" + ] + }, + { + "cell_type": "markdown", + "id": "ad8f304b", + "metadata": {}, + "source": [ + "## Exploratory data analysis\n", + "\n", + "We consolidate the annotated spans into a tabular view to inspect label and subcategory coverage before running any retrieval experiments.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d99c5871", + "metadata": {}, + "outputs": [], + "source": [ + "def samples_to_dataframe(samples):\n", + " rows = []\n", + " for label, items in samples.items():\n", + " for item in items:\n", + " rows.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"labels\": item[\"labels\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"n_choices\": len(item[\"choices\"]),\n", + " \"char_len\": len(item[\"text\"]),\n", + " }\n", + " )\n", + " return pd.DataFrame(rows)\n", + "\n", + "\n", + "samples_df = samples_to_dataframe(samples_by_label)\n", + "samples_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0eca77e", + "metadata": {}, + "outputs": [], + "source": [ + "samples_df[\"labels\"].map(len).describe()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbe01d41", + "metadata": {}, + "outputs": [], + "source": [ + "samples_df[\"choices\"].map(len).describe()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b898f23c", + "metadata": {}, + "outputs": [], + "source": [ + "samples_df.loc[samples_df[\"n_choices\"] == 0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fefb83ad", + "metadata": {}, + "outputs": [], + "source": [ + "# Remove samples with zero choices\n", + "samples_df = samples_df[samples_df[\"n_choices\"] > 0].reset_index(drop=True)\n", + "samples_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2aa3550f", + "metadata": {}, + "outputs": [], + "source": [ + "for label, samples in samples_by_label.items():\n", + " print(f\"{label}: {len(samples)} samples before filtering\")\n", + " samples_by_label[label] = [\n", + " sample for sample in samples if len(sample[\"choices\"]) > 0\n", + " ]\n", + " print(f\"{label}: {len(samples_by_label[label])} samples after filtering\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12ff76ac", + "metadata": {}, + "outputs": [], + "source": [ + "samples_df.loc[samples_df[\"n_choices\"] > 1, [\"text\", \"choices\"]].explode(\n", + " \"choices\"\n", + ").drop_duplicates()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1bb2dfec", + "metadata": {}, + "outputs": [], + "source": [ + "label_counts = samples_df.groupby(\"label\").size().sort_values(ascending=False)\n", + "choice_counts = (\n", + " samples_df.explode(\"choices\")\n", + " .groupby([\"label\", \"choices\"])\n", + " .size()\n", + " .rename(\"count\")\n", + ")\n", + "\n", + "summary_table = pd.DataFrame(\n", + " {\n", + " \"samples\": label_counts,\n", + " \"unique_choices\": choice_counts.groupby(\"label\").size(),\n", + " \"avg_text_len\": samples_df.groupby(\"label\")[\"char_len\"]\n", + " .mean()\n", + " .round(1),\n", + " }\n", + ")\n", + "summary_table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "729dc02e", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "order = label_counts.index\n", + "sns.barplot(\n", + " x=label_counts.values, y=label_counts.index, ax=ax, palette=\"viridis\"\n", + ")\n", + "ax.set_title(\"Samples per label\")\n", + "ax.set_xlabel(\"Number of samples\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "plot_data = (\n", + " choice_counts.reset_index()\n", + " .sort_values(\"count\", ascending=False)\n", + " .groupby(\"label\")\n", + " .head(10)\n", + ")\n", + "\n", + "for label in order:\n", + " subset = plot_data.query(\"label == @label\").sort_values(\n", + " \"count\", ascending=True\n", + " )\n", + " fig, ax = plt.subplots(figsize=(8, 4))\n", + " sns.barplot(data=subset, x=\"count\", y=\"choices\", palette=\"viridis\", ax=ax)\n", + " ax.set_title(f\"Top subcategories for {label}\")\n", + " ax.set_xlabel(\"Frequency\")\n", + " ax.set_ylabel(\"Subcategory\")\n", + " ax.grid(axis=\"x\", alpha=0.2)\n", + " ax.invert_yaxis()\n", + " plt.tight_layout()\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61ef49f8", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "sns.boxplot(data=samples_df, x=\"label\", y=\"char_len\", palette=\"viridis\")\n", + "ax.set_title(\"Entity span length by label\")\n", + "ax.set_xlabel(\"Label\")\n", + "ax.set_ylabel(\"Character length\")\n", + "ax.set_ylim(0, 100)\n", + "plt.xticks(rotation=15)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a402956", + "metadata": {}, + "outputs": [], + "source": [ + "for label in label_counts.index:\n", + " print(f\"=== {label} ===\")\n", + " subset = samples_df.query(\"label == @label\")\n", + " for _, row in subset.sample(n=min(3, len(subset))).iterrows():\n", + " print(f\"- Text: {row['text']}\")\n", + " print(f\" Choices: {row['choices']}\")\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "535f6000", + "metadata": {}, + "source": [ + "## USEM pipeline audit\n", + "\n", + "We validate the existing TensorFlow-based USEM subcategorizer by reloading the cached assets, recomputing embeddings, and measuring retrieval accuracy before swapping in PyTorch alternatives." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51e8f65e", + "metadata": {}, + "outputs": [], + "source": [ + "pipeline_path = Path(\n", + " \"/resources/pipelines/production/full-paragraph/pipeline.json\"\n", + ")\n", + "\n", + "with pipeline_path.open() as f:\n", + " pipeline_config = json.load(f)\n", + "\n", + "usem_configs = {\n", + " config[1][\"category\"]: {\n", + " \"subcategories_path\": config[1][\"subcategories_path\"],\n", + " \"response_embeddings_path\": config[1][\"response_embeddings_path\"],\n", + " }\n", + " for config in pipeline_config[\"postprocess\"]\n", + " if \"USEMSubcategorizer\" in config[0]\n", + "}\n", + "\n", + "usem_configs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ab1c51f", + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.transforms.entity_subcategories.usem import USEMSubcategorizer\n", + "\n", + "usem_models = {}\n", + "for label, cfg in usem_configs.items():\n", + " print(f\"Loading USEM assets for {label}…\")\n", + " usem_models[label] = USEMSubcategorizer(category=label, **cfg)\n", + "\n", + "list(usem_models.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d28103d8", + "metadata": {}, + "outputs": [], + "source": [ + "model = usem_models[\"CONDUCTA\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f07e07f9", + "metadata": {}, + "outputs": [], + "source": [ + "model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0cd9987", + "metadata": {}, + "outputs": [], + "source": [ + "model.subcategories" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f51be1d", + "metadata": {}, + "outputs": [], + "source": [ + "# Sample encoding of first subcategory\n", + "encoded = model.usem.encode(\n", + " [model.subcategories[0].replace(\"_\", \" \")],\n", + " context_array=[model.subcategories[0].replace(\"_\", \" \")],\n", + " encoder_type=\"response_encoder\",\n", + ")\n", + "encoded" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "caa86d26", + "metadata": {}, + "outputs": [], + "source": [ + "def cosine_similarity(a, b):\n", + " a_norm = a / np.linalg.norm(a, axis=1, keepdims=True)\n", + " b_norm = b / np.linalg.norm(b, axis=1, keepdims=True)\n", + " return np.sum(a_norm * b_norm, axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d5dc56a", + "metadata": {}, + "outputs": [], + "source": [ + "# Compute cosine similarity matrix - first value should be 1\n", + "cosine_similarity(encoded, model.usem_vectors)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb470374", + "metadata": {}, + "outputs": [], + "source": [ + "import unicodedata\n", + "\n", + "\n", + "def normalize_text(text: str) -> str:\n", + " \"\"\"Normalize text by lowercasing and removing non-alphanumeric characters.\"\"\"\n", + " text = text.lower()\n", + " text = \"\".join(\n", + " char\n", + " for char in unicodedata.normalize(\"NFKD\", text)\n", + " if char.isalnum() or char.isspace()\n", + " )\n", + " return text\n", + "\n", + "\n", + "def normalize_subcategory(name: str) -> str:\n", + " \"\"\"Mirror the preprocessing applied when the cached embeddings were built.\"\"\"\n", + " # name = normalize_text(name)\n", + " return name.replace(\"_\", \" \")\n", + "\n", + "\n", + "def recompute_response_vectors(model):\n", + " \"\"\"Return normalized subcategory strings and freshly encoded vectors.\"\"\"\n", + " normalized = [\n", + " normalize_subcategory(subcategory)\n", + " for subcategory in model.subcategories\n", + " ]\n", + " fresh_vectors = model.usem.encode(\n", + " normalized,\n", + " context_array=normalized,\n", + " encoder_type=\"response_encoder\",\n", + " )\n", + " return normalized, fresh_vectors.numpy()\n", + "\n", + "\n", + "cached = model.usem_vectors\n", + "normalized_subcategories, fresh = recompute_response_vectors(model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7362249", + "metadata": {}, + "outputs": [], + "source": [ + "def l2_normalize(matrix: np.ndarray) -> np.ndarray:\n", + " \"\"\"Row-wise L2 normalization to prepare cosine similarity checks.\"\"\"\n", + " return matrix / np.linalg.norm(matrix, axis=1, keepdims=True)\n", + "\n", + "\n", + "cached_norm = l2_normalize(cached)\n", + "fresh_norm = l2_normalize(fresh)\n", + "cosine_matrix = cached_norm @ fresh_norm.T\n", + "cosine_matrix.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a755a678", + "metadata": {}, + "outputs": [], + "source": [ + "row_max_indices = np.argmax(cosine_matrix, axis=1)\n", + "row_max_values = cosine_matrix[\n", + " np.arange(len(row_max_indices)), row_max_indices\n", + "]\n", + "mismatched_rows = np.where(row_max_indices != np.arange(len(row_max_indices)))[\n", + " 0\n", + "]\n", + "diag_values = np.diag(cosine_matrix)\n", + "off_identity_matches = (\n", + " pd.DataFrame(\n", + " {\n", + " \"subcategory\": [\n", + " model.subcategories[idx] for idx in mismatched_rows\n", + " ],\n", + " \"matched_index\": row_max_indices[mismatched_rows],\n", + " \"max_cosine\": row_max_values[mismatched_rows],\n", + " \"diagonal_cosine\": diag_values[mismatched_rows],\n", + " }\n", + " )\n", + " if mismatched_rows.size\n", + " else pd.DataFrame(\n", + " columns=[\n", + " \"subcategory\",\n", + " \"matched_index\",\n", + " \"max_cosine\",\n", + " \"diagonal_cosine\",\n", + " ]\n", + " )\n", + ")\n", + "off_identity_matches # .head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50f14d8c", + "metadata": {}, + "outputs": [], + "source": [ + "diag_values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a42e0a8", + "metadata": {}, + "outputs": [], + "source": [ + "tolerance = 0.99\n", + "near_identity_mask = diag_values >= tolerance\n", + "print(\n", + " f\"Diagonal entries at or above {tolerance:.2f}: {near_identity_mask.sum()} / {len(diag_values)}\"\n", + ")\n", + "diagonal_gaps = (\n", + " pd.DataFrame(\n", + " {\n", + " \"subcategory\": [\n", + " model.subcategories[idx]\n", + " for idx, ok in enumerate(near_identity_mask)\n", + " if not ok\n", + " ],\n", + " \"diagonal_cosine\": diag_values[~near_identity_mask],\n", + " \"shortfall\": 1 - diag_values[~near_identity_mask],\n", + " }\n", + " )\n", + " if (~near_identity_mask).any()\n", + " else pd.DataFrame(columns=[\"subcategory\", \"diagonal_cosine\", \"shortfall\"])\n", + ")\n", + "diagonal_gaps # .head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "129e8489", + "metadata": {}, + "outputs": [], + "source": [ + "embedding_checks = []\n", + "for label, model in usem_models.items():\n", + " cached = model.usem_vectors\n", + " normalized_subcategories, fresh = recompute_response_vectors(model)\n", + " cached_norm = l2_normalize(cached)\n", + " fresh_norm = l2_normalize(fresh)\n", + " delta = cached - fresh\n", + " row_l2 = np.linalg.norm(delta, axis=1)\n", + " cos = np.sum(cached_norm * fresh_norm, axis=1)\n", + " embedding_checks.append(\n", + " {\n", + " \"label\": label,\n", + " \"max_l2\": float(row_l2.max()),\n", + " \"mean_l2\": float(row_l2.mean()),\n", + " \"min_cos\": float(cos.min()),\n", + " \"mean_cos\": float(cos.mean()),\n", + " }\n", + " )\n", + "\n", + "embedding_checks_df = pd.DataFrame(embedding_checks).set_index(\"label\")\n", + "embedding_checks_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5c521a0", + "metadata": {}, + "outputs": [], + "source": [ + "# Using batch retrieval\n", + "def retrieve_samples(samples, models, top_k=5):\n", + " records = []\n", + " for label, items in samples.items():\n", + " model = models[label]\n", + " texts = [item[\"text\"] for item in items]\n", + " retrieved_lists = model.batch_retrieve(texts, top_k=top_k)\n", + " for item, retrieved in zip(items, retrieved_lists):\n", + " records.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"retrieved\": retrieved,\n", + " }\n", + " )\n", + " return records\n", + "\n", + "\n", + "retrieval_records = retrieve_samples(samples_by_label, usem_models, top_k=5)\n", + "len(retrieval_records)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5278ead4", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter out samples with no annotated choices\n", + "retrieval_records = [\n", + " record for record in retrieval_records if record[\"choices\"]\n", + "]\n", + "len(retrieval_records)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efbe083b", + "metadata": {}, + "outputs": [], + "source": [ + "def compute_topk_accuracy(records, ks=(1, 2, 3, 4, 5)):\n", + " metrics = []\n", + " labels = sorted({record[\"label\"] for record in records})\n", + " for label in labels:\n", + " label_records = [\n", + " record for record in records if record[\"label\"] == label\n", + " ]\n", + " total = len(label_records)\n", + " for k in ks:\n", + " hits = sum(\n", + " any(\n", + " choice in record[\"retrieved\"][:k]\n", + " for choice in record[\"choices\"]\n", + " )\n", + " for record in label_records\n", + " )\n", + " metrics.append(\n", + " {\n", + " \"label\": label,\n", + " \"k\": k,\n", + " \"accuracy\": hits / total if total else np.nan,\n", + " }\n", + " )\n", + "\n", + " overall = []\n", + " for k in ks:\n", + " hits = sum(\n", + " any(\n", + " choice in record[\"retrieved\"][:k]\n", + " for choice in record[\"choices\"]\n", + " )\n", + " for record in records\n", + " )\n", + " overall.append(\n", + " {\"label\": \"OVERALL\", \"k\": k, \"accuracy\": hits / len(records)}\n", + " )\n", + "\n", + " metrics.extend(overall)\n", + " return pd.DataFrame(metrics)\n", + "\n", + "\n", + "baseline_accuracy = compute_topk_accuracy(retrieval_records)\n", + "baseline_accuracy_pivot = baseline_accuracy.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + ").sort_index()\n", + "\n", + "baseline_accuracy_pivot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa531a6c", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "\n", + "sns.heatmap(\n", + " baseline_accuracy_pivot.loc[\n", + " [\n", + " label\n", + " for label in baseline_accuracy_pivot.index\n", + " if label != \"OVERALL\"\n", + " ]\n", + " ],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"USEM baseline top-k accuracy by label\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "baseline_accuracy_pivot.loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f775ee51", + "metadata": {}, + "outputs": [], + "source": [ + "# For each label, show some random success cases where the correct choice was retrieved in top 5\n", + "for label in target_labels:\n", + " print(f\"=== {label} ===\")\n", + " label_records = [\n", + " record\n", + " for record in retrieval_records\n", + " if record[\"label\"] == label\n", + " and any(\n", + " choice in record[\"retrieved\"][:5] for choice in record[\"choices\"]\n", + " )\n", + " ]\n", + " for record in random.sample(label_records, k=min(3, len(label_records))):\n", + " print(f\"- Text: {record['text']}\")\n", + " print(f\" Choices: {record['choices']}\")\n", + " print(f\" Retrieved: {record['retrieved'][:5]}\")\n", + " print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "441f8b7d", + "metadata": {}, + "outputs": [], + "source": [ + "# For each label, show some random failure cases where the correct choice was not retrieved in top 5\n", + "for label in target_labels:\n", + " print(f\"=== {label} ===\")\n", + " label_records = [\n", + " record\n", + " for record in retrieval_records\n", + " if record[\"label\"] == label\n", + " and not any(\n", + " choice in record[\"retrieved\"][:5] for choice in record[\"choices\"]\n", + " )\n", + " ]\n", + " for record in random.sample(label_records, k=min(3, len(label_records))):\n", + " print(f\"- Text: {record['text']}\")\n", + " print(f\" Choices: {record['choices']}\")\n", + " print(f\" Retrieved: {record['retrieved'][:5]}\")\n", + " print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac517e27", + "metadata": {}, + "outputs": [], + "source": [ + "overall = baseline_accuracy_pivot.loc[\"OVERALL\"]\n", + "label_top1 = baseline_accuracy_pivot.loc[\n", + " [label for label in baseline_accuracy_pivot.index if label != \"OVERALL\"], 1\n", + "]\n", + "\n", + "summary_md = f\"\"\"\n", + "### Baseline findings\n", + "\n", + "- Overall top-1 accuracy: {overall[1]:.2%}\n", + "- Overall top-3 accuracy: {overall[3]:.2%}\n", + "- Overall top-5 accuracy: {overall[5]:.2%}\n", + "- Best-performing label (top-1): {label_top1.idxmax()} at {label_top1.max():.2%}\n", + "- Most challenging label (top-1): {label_top1.idxmin()} at {label_top1.min():.2%}\n", + "\n", + "#### Next steps\n", + "1. Replace the TensorFlow encoder with sentence-transformer checkpoints and repeat the evaluation.\n", + "2. Compare latency and memory footprint during retrieval for each contender.\n", + "3. Consolidate the winning model into the pipeline and update deployment assets.\n", + "\"\"\"\n", + "\n", + "Markdown(summary_md)" + ] + }, + { + "cell_type": "markdown", + "id": "4d794421", + "metadata": {}, + "source": [ + "## Random Retriever" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "250cabac", + "metadata": {}, + "outputs": [], + "source": [ + "class RandomRetriever:\n", + " def __init__(self, subcategories: list[str], seed: int = 42):\n", + " self.subcategories = subcategories\n", + " self._rng = random.Random(seed)\n", + "\n", + " def batch_retrieve(\n", + " self, texts: list[str], top_k: int = 5\n", + " ) -> list[list[str]]:\n", + " k = min(top_k, len(self.subcategories))\n", + " if k == 0:\n", + " return [[] for _ in texts]\n", + " return [self._rng.sample(self.subcategories, k) for _ in texts]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f556454", + "metadata": {}, + "outputs": [], + "source": [ + "label_subcategories = {\n", + " label: model.subcategories for label, model in usem_models.items()\n", + "}\n", + "random_models = {\n", + " label: RandomRetriever(subcats, seed=idx * 17)\n", + " for idx, (label, subcats) in enumerate(label_subcategories.items())\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecc98fde", + "metadata": {}, + "outputs": [], + "source": [ + "baseline_variants = {\n", + " \"Random\": random_models,\n", + " # \"USEM\": usem_models,\n", + " # \"BM25\": bm25_models,\n", + " # \"USEM + BM25 hybrid\": hybrid_models,\n", + "}\n", + "\n", + "variant_records = {}\n", + "for variant, models in baseline_variants.items():\n", + " records = retrieve_samples(samples_by_label, models, top_k=5)\n", + " variant_records[variant] = records\n", + "\n", + "variant_accuracy = {\n", + " variant: compute_topk_accuracy(records)\n", + " for variant, records in variant_records.items()\n", + "}\n", + "variant_pivots = {\n", + " variant: df.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + " ).sort_index()\n", + " for variant, df in variant_accuracy.items()\n", + "}\n", + "\n", + "for variant, pivot in variant_pivots.items():\n", + " display(Markdown(f\"**{variant} baseline top-k accuracy**\"))\n", + " display(pivot)\n", + "\n", + "# combined_series = {\n", + "# \"TensorFlow USEM\": baseline_accuracy_pivot.loc[\"OVERALL\"],\n", + "# \"DistilUSE\": distiluse_accuracy_pivot.loc[\"OVERALL\"],\n", + "# \"MiniLM\": minilm_accuracy_pivot.loc[\"OVERALL\"],\n", + "# }\n", + "# combined_series.update(\n", + "# {variant: pivot.loc[\"OVERALL\"] for variant, pivot in variant_pivots.items()}\n", + "# )\n", + "# combined_accuracy = pd.DataFrame.from_dict(combined_series, orient=\"index\")\n", + "# combined_accuracy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "943672c8", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "\n", + "sns.heatmap(\n", + " variant_pivots[\"Random\"].loc[\n", + " [\n", + " label\n", + " for label in baseline_accuracy_pivot.index\n", + " if label != \"OVERALL\"\n", + " ]\n", + " ],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"Random baseline top-k accuracy by label\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "variant_pivots[\"Random\"].loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "markdown", + "id": "f3ead018", + "metadata": {}, + "source": [ + "## BM25 retriever" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7b46e82", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import re\n", + "from collections import Counter\n", + "\n", + "token_pattern = re.compile(r\"\\w+\", re.UNICODE)\n", + "\n", + "\n", + "def tokenize(text: str) -> list[str]:\n", + " return token_pattern.findall(normalize_text(text))\n", + "\n", + "\n", + "class BM25Retriever:\n", + " def __init__(\n", + " self, subcategories: list[str], k1: float = 1.2, b: float = 0.75\n", + " ):\n", + " self.subcategories = subcategories\n", + " normalized = [normalize_subcategory(sub) for sub in subcategories]\n", + " self.tokenized = [tokenize(text) for text in normalized]\n", + " self.doc_len = [len(tokens) for tokens in self.tokenized]\n", + " self.avgdl = sum(self.doc_len) / len(self.doc_len)\n", + " self.k1 = k1\n", + " self.b = b\n", + " self.N = len(self.tokenized)\n", + " self.doc_freqs = [Counter(tokens) for tokens in self.tokenized]\n", + " corpus_tokens = set().union(*self.doc_freqs)\n", + " self.df = Counter(\n", + " {\n", + " token: sum(1 for doc in self.doc_freqs if token in doc)\n", + " for token in corpus_tokens\n", + " }\n", + " )\n", + " self.idf = {\n", + " token: math.log(1 + (self.N - freq + 0.5) / (freq + 0.5))\n", + " for token, freq in self.df.items()\n", + " }\n", + "\n", + " def _score(self, tokens: list[str]) -> np.ndarray:\n", + " scores = np.zeros(self.N, dtype=np.float64)\n", + " for token in tokens:\n", + " idf = self.idf.get(token)\n", + " if idf is None:\n", + " continue\n", + " for idx, freq_map in enumerate(self.doc_freqs):\n", + " freq = freq_map.get(token, 0)\n", + " if freq == 0:\n", + " continue\n", + " denom = freq + self.k1 * (\n", + " 1 - self.b + self.b * self.doc_len[idx] / self.avgdl\n", + " )\n", + " scores[idx] += idf * freq * (self.k1 + 1) / denom\n", + " return scores\n", + "\n", + " def batch_retrieve(\n", + " self, texts: list[str], top_k: int = 5\n", + " ) -> list[list[str]]:\n", + " return [self._topk(text, top_k) for text in texts]\n", + "\n", + " def _topk(self, text: str, top_k: int) -> list[str]:\n", + " scores = self._score(tokenize(text))\n", + " if top_k <= 0:\n", + " return []\n", + " top_indices = np.argsort(-scores)[:top_k]\n", + " return [self.subcategories[idx] for idx in top_indices]\n", + "\n", + " def score_vector(self, text: str) -> np.ndarray:\n", + " return self._score(tokenize(text))\n", + "\n", + "\n", + "class HybridRetriever:\n", + " def __init__(self, usem_model, bm25_model, bm25_weight: float = 0.5):\n", + " self.usem_model = usem_model\n", + " self.bm25_model = bm25_model\n", + " self.response_vectors = usem_model.usem_vectors\n", + " self.bm25_weight = bm25_weight\n", + "\n", + " def batch_retrieve(\n", + " self, texts: list[str], top_k: int = 5\n", + " ) -> list[list[str]]:\n", + " query_vectors = self.usem_model.usem.batch_encode(\n", + " [normalize_text(text) for text in texts],\n", + " encoder_type=\"question_encoder\",\n", + " )\n", + " similarity = np.inner(query_vectors, self.response_vectors)\n", + " combined_results = []\n", + " for row_idx, text in enumerate(texts):\n", + " bm25_scores = self.bm25_model.score_vector(text)\n", + " bm25_norm = bm25_scores / (bm25_scores.max() + 1e-9)\n", + " sim_scores = similarity[row_idx]\n", + " sim_norm = sim_scores / (sim_scores.max() + 1e-9)\n", + " combined = (\n", + " self.bm25_weight * bm25_norm\n", + " + (1 - self.bm25_weight) * sim_norm\n", + " )\n", + " k = min(top_k, combined.shape[0])\n", + " top_indices = np.argsort(-combined)[:k]\n", + " combined_results.append(\n", + " [self.bm25_model.subcategories[idx] for idx in top_indices]\n", + " )\n", + " return combined_results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b629aa3", + "metadata": {}, + "outputs": [], + "source": [ + "bm25_models = {\n", + " label: BM25Retriever(subcats)\n", + " for label, subcats in label_subcategories.items()\n", + "}\n", + "hybrid_models = {\n", + " label: HybridRetriever(\n", + " usem_models[label], bm25_models[label], bm25_weight=0.4\n", + " )\n", + " for label in usem_models\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ccfc969e", + "metadata": {}, + "outputs": [], + "source": [ + "baseline_variants = {\n", + " \"Random\": random_models,\n", + " \"BM25\": bm25_models,\n", + " \"USEM + BM25 hybrid\": hybrid_models,\n", + "}\n", + "\n", + "variant_records = {}\n", + "for variant, models in baseline_variants.items():\n", + " records = retrieve_samples(samples_by_label, models, top_k=5)\n", + " variant_records[variant] = records\n", + "\n", + "variant_accuracy = {\n", + " variant: compute_topk_accuracy(records)\n", + " for variant, records in variant_records.items()\n", + "}\n", + "variant_pivots = {\n", + " variant: df.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + " ).sort_index()\n", + " for variant, df in variant_accuracy.items()\n", + "}\n", + "\n", + "for variant, pivot in variant_pivots.items():\n", + " display(Markdown(f\"**{variant} baseline top-k accuracy**\"))\n", + " display(pivot)\n", + "\n", + " fig, ax = plt.subplots()\n", + "\n", + " sns.heatmap(\n", + " pivot.loc[\n", + " [\n", + " label\n", + " for label in baseline_accuracy_pivot.index\n", + " if label != \"OVERALL\"\n", + " ]\n", + " ],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + " )\n", + " ax.set_title(f\"{variant} baseline top-k accuracy by label\")\n", + " ax.set_xlabel(\"k\")\n", + " ax.set_ylabel(\"Label\")\n", + " plt.tight_layout()\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ffbdfc8a", + "metadata": {}, + "source": [ + "## Challenger models: PyTorch sentence-transformers\n", + "\n", + "We now evaluate two PyTorch-based encoders as potential replacements for the TensorFlow USEM model:\n", + "- **DistilUSE**: Knowledge-distilled USE producing 512-dimensional embeddings\n", + "- **MultilingualMiniLM**: Higher-quality MiniLM producing 384-dimensional embeddings\n", + "\n", + "Both models support Apple Silicon (M1/M2/M3) via MPS backend and eliminate the TensorFlow dependency." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f22169f", + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.models.usem.sentence_transformers_encoder import (\n", + " DistilUSEEncoder,\n", + " MultilingualMiniLMEncoder,\n", + ")\n", + "\n", + "# Initialize challenger encoders\n", + "distiluse_encoder = DistilUSEEncoder()\n", + "minilm_encoder = MultilingualMiniLMEncoder()\n", + "\n", + "challenger_encoders = {\n", + " \"DistilUSE\": distiluse_encoder,\n", + " \"MiniLM\": minilm_encoder,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "016fcb42", + "metadata": {}, + "outputs": [], + "source": [ + "from hashlib import md5\n", + "\n", + "from aymurai.utils.download import download\n", + "from aymurai.utils.misc import is_url\n", + "\n", + "\n", + "class SentenceTransformerRetriever:\n", + " def __init__(\n", + " self,\n", + " label,\n", + " subcategories_path,\n", + " encoder,\n", + " encoder_name=\"challenger\",\n", + " batch_size=256,\n", + " ):\n", + " self.label = label\n", + " self.encoder = encoder\n", + " self.batch_size = batch_size\n", + " self.encoder_name = encoder_name\n", + " self.subcategories = self._load_subcategories(subcategories_path)\n", + " normalized_subcategories = [\n", + " normalize_subcategory(sub) for sub in self.subcategories\n", + " ]\n", + " self.response_vectors = self.encoder.batch_encode(\n", + " normalized_subcategories,\n", + " encoder_type=\"response_encoder\",\n", + " batch_size=batch_size,\n", + " )\n", + "\n", + " def _load_subcategories(self, path):\n", + " resolved_path = Path(path)\n", + " if is_url(path):\n", + " cache_root = Path(\n", + " os.getenv(\"AYMURAI_CACHE_BASEPATH\", \"/resources/cache/aymurai\")\n", + " )\n", + " target_dir = cache_root / \"sentence_transformer_retriever\"\n", + " target_dir.mkdir(parents=True, exist_ok=True)\n", + " target_path = target_dir / md5(path.encode(\"utf-8\")).hexdigest()\n", + " resolved_path = Path(download(path, str(target_path)))\n", + " if not resolved_path.exists():\n", + " raise FileNotFoundError(\n", + " f\"Subcategory file not found for {self.label}: {resolved_path}\"\n", + " )\n", + " with resolved_path.open() as file:\n", + " return [line.strip() for line in file if line.strip()]\n", + "\n", + " def batch_retrieve(self, texts, top_k=5):\n", + " if not texts:\n", + " return []\n", + "\n", + " query_vectors = self.encoder.batch_encode(\n", + " texts,\n", + " encoder_type=\"question_encoder\",\n", + " batch_size=self.batch_size,\n", + " )\n", + " similarity = np.inner(query_vectors, self.response_vectors)\n", + " k = min(top_k, similarity.shape[1])\n", + " top_indices = np.argsort(-similarity, axis=1)[:, :k]\n", + " return [\n", + " [self.subcategories[idx] for idx in row] for row in top_indices\n", + " ]\n", + "\n", + "\n", + "class HybridSentenceTransformerRetriever:\n", + " def __init__(self, base_retriever, bm25_model, bm25_weight: float = 0.4):\n", + " self.base_retriever = base_retriever\n", + " self.bm25_model = bm25_model\n", + " self.bm25_weight = bm25_weight\n", + " self.encoder = base_retriever.encoder\n", + " self.response_vectors = base_retriever.response_vectors\n", + " self.subcategories = base_retriever.subcategories\n", + " self.batch_size = base_retriever.batch_size\n", + "\n", + " def batch_retrieve(self, texts, top_k=5):\n", + " if not texts:\n", + " return []\n", + "\n", + " query_vectors = self.encoder.batch_encode(\n", + " texts,\n", + " encoder_type=\"question_encoder\",\n", + " batch_size=self.batch_size,\n", + " )\n", + " similarity = np.inner(query_vectors, self.response_vectors)\n", + " combined_results = []\n", + " for row_idx, text in enumerate(texts):\n", + " bm25_scores = self.bm25_model.score_vector(text)\n", + " bm25_norm = bm25_scores / (bm25_scores.max() + 1e-9)\n", + " sim_scores = similarity[row_idx]\n", + " sim_norm = sim_scores / (sim_scores.max() + 1e-9)\n", + " combined = (\n", + " self.bm25_weight * bm25_norm\n", + " + (1 - self.bm25_weight) * sim_norm\n", + " )\n", + " k = min(top_k, combined.shape[0])\n", + " top_indices = np.argsort(-combined)[:k]\n", + " combined_results.append(\n", + " [self.subcategories[idx] for idx in top_indices]\n", + " )\n", + " return combined_results\n", + "\n", + "\n", + "def build_challenger_models(\n", + " usem_configs, encoder, encoder_name, batch_size=256\n", + "):\n", + " return {\n", + " label: SentenceTransformerRetriever(\n", + " label=label,\n", + " subcategories_path=cfg[\"subcategories_path\"],\n", + " encoder=encoder,\n", + " encoder_name=encoder_name,\n", + " batch_size=batch_size,\n", + " )\n", + " for label, cfg in usem_configs.items()\n", + " }\n", + "\n", + "\n", + "def build_hybrid_sentence_transformer_models(\n", + " base_models, bm25_models, bm25_weight=0.4\n", + "):\n", + " return {\n", + " label: HybridSentenceTransformerRetriever(\n", + " base_retriever=base_models[label],\n", + " bm25_model=bm25_models[label],\n", + " bm25_weight=bm25_weight,\n", + " )\n", + " for label in base_models\n", + " }\n", + "\n", + "\n", + "def evaluate_sentence_transformers(samples, models, top_k=5):\n", + " records = []\n", + " for label, items in samples.items():\n", + " retriever = models[label]\n", + " texts = [item[\"text\"] for item in items]\n", + " retrieved_lists = retriever.batch_retrieve(texts, top_k=top_k)\n", + " for item, retrieved in zip(items, retrieved_lists):\n", + " records.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"retrieved\": retrieved,\n", + " }\n", + " )\n", + " return records\n", + "\n", + "\n", + "distiluse_models = build_challenger_models(\n", + " usem_configs, distiluse_encoder, \"DistilUSE\"\n", + ")\n", + "minilm_models = build_challenger_models(usem_configs, minilm_encoder, \"MiniLM\")" + ] + }, + { + "cell_type": "markdown", + "id": "4847338b", + "metadata": {}, + "source": [ + "### DistilUSE evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3baaedfb", + "metadata": {}, + "outputs": [], + "source": [ + "distiluse_retrieval_records = evaluate_sentence_transformers(\n", + " samples_by_label,\n", + " distiluse_models,\n", + " top_k=5,\n", + ")\n", + "distiluse_accuracy = compute_topk_accuracy(distiluse_retrieval_records)\n", + "distiluse_accuracy_pivot = distiluse_accuracy.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + ").sort_index()\n", + "\n", + "distiluse_accuracy_pivot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30162b63", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "sns.heatmap(\n", + " distiluse_accuracy_pivot.loc[\n", + " [\n", + " label\n", + " for label in distiluse_accuracy_pivot.index\n", + " if label != \"OVERALL\"\n", + " ]\n", + " ],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"DistilUSE top-k accuracy by label\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "distiluse_accuracy_pivot.loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "markdown", + "id": "a13c5d69", + "metadata": {}, + "source": [ + "### MultilingualMiniLM evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd0e59f1", + "metadata": {}, + "outputs": [], + "source": [ + "minilm_retrieval_records = evaluate_sentence_transformers(\n", + " samples_by_label,\n", + " minilm_models,\n", + " top_k=5,\n", + ")\n", + "minilm_accuracy = compute_topk_accuracy(minilm_retrieval_records)\n", + "minilm_accuracy_pivot = minilm_accuracy.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + ").sort_index()\n", + "\n", + "minilm_accuracy_pivot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e9fe5a7", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "sns.heatmap(\n", + " minilm_accuracy_pivot.loc[\n", + " [label for label in minilm_accuracy_pivot.index if label != \"OVERALL\"]\n", + " ],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"MultilingualMiniLM top-k accuracy by label\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "minilm_accuracy_pivot.loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "markdown", + "id": "4593cd14", + "metadata": {}, + "source": [ + "### Hybrid (BM25 + encoder) evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e58869d", + "metadata": {}, + "outputs": [], + "source": [ + "distiluse_hybrid_models = build_hybrid_sentence_transformer_models(\n", + " distiluse_models,\n", + " bm25_models,\n", + " bm25_weight=0.4,\n", + ")\n", + "minilm_hybrid_models = build_hybrid_sentence_transformer_models(\n", + " minilm_models,\n", + " bm25_models,\n", + " bm25_weight=0.4,\n", + ")\n", + "\n", + "distiluse_hybrid_records = evaluate_sentence_transformers(\n", + " samples_by_label,\n", + " distiluse_hybrid_models,\n", + " top_k=5,\n", + ")\n", + "distiluse_hybrid_accuracy = compute_topk_accuracy(distiluse_hybrid_records)\n", + "distiluse_hybrid_pivot = distiluse_hybrid_accuracy.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + ").sort_index()\n", + "\n", + "distiluse_hybrid_pivot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4c103f8", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "sns.heatmap(\n", + " distiluse_hybrid_pivot.loc[\n", + " [label for label in distiluse_hybrid_pivot.index if label != \"OVERALL\"]\n", + " ],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"DistilUSE + BM25 hybrid top-k accuracy by label\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "distiluse_hybrid_pivot.loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c249abb", + "metadata": {}, + "outputs": [], + "source": [ + "minilm_hybrid_records = evaluate_sentence_transformers(\n", + " samples_by_label,\n", + " minilm_hybrid_models,\n", + " top_k=5,\n", + ")\n", + "minilm_hybrid_accuracy = compute_topk_accuracy(minilm_hybrid_records)\n", + "minilm_hybrid_pivot = minilm_hybrid_accuracy.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + ").sort_index()\n", + "\n", + "minilm_hybrid_pivot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5333bf04", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "sns.heatmap(\n", + " minilm_hybrid_pivot.loc[\n", + " [label for label in minilm_hybrid_pivot.index if label != \"OVERALL\"]\n", + " ],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"MiniLM + BM25 hybrid top-k accuracy by label\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "minilm_hybrid_pivot.loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "markdown", + "id": "6e4f967a", + "metadata": {}, + "source": [ + "## Model comparison and champion selection\n", + "\n", + "We compare the three models across all metrics to select the best replacement for the TensorFlow USEM implementation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ef3d532", + "metadata": {}, + "outputs": [], + "source": [ + "# Consolidate OVERALL accuracy across all models\n", + "comparison_df = pd.DataFrame(\n", + " {\n", + " \"TensorFlow USEM\": baseline_accuracy_pivot.loc[\"OVERALL\"],\n", + " \"DistilUSE\": distiluse_accuracy_pivot.loc[\"OVERALL\"],\n", + " \"MiniLM\": minilm_accuracy_pivot.loc[\"OVERALL\"],\n", + " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"].loc[\n", + " \"OVERALL\"\n", + " ],\n", + " \"DistilUSE + BM25\": distiluse_hybrid_pivot.loc[\"OVERALL\"],\n", + " \"MiniLM + BM25\": minilm_hybrid_pivot.loc[\"OVERALL\"],\n", + " }\n", + ").T\n", + "\n", + "comparison_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d90c448", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize comparison across k values as bar chart, sorted by performance at each k\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "melted = comparison_df.reset_index().melt(\n", + " id_vars=\"index\", var_name=\"k\", value_name=\"accuracy\"\n", + ")\n", + "\n", + "sns.barplot(\n", + " data=melted.sort_values(by=\"accuracy\", ascending=False),\n", + " x=\"k\",\n", + " y=\"accuracy\",\n", + " hue=\"index\",\n", + " ax=ax,\n", + ")\n", + "\n", + "ax.set_title(\"Top-k Accuracy Comparison Across Models\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Accuracy\")\n", + "ax.set_ylim(0, 1)\n", + "ax.legend(title=\"Model\", bbox_to_anchor=(1.05, 1), loc=\"upper left\")\n", + "ax.grid(axis=\"y\", alpha=0.3)\n", + "plt.xticks(rotation=0)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b42a256", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize comparison across k values as bar chart, sorted by performance at each k\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "melted = comparison_df.reset_index().melt(\n", + " id_vars=\"index\", var_name=\"k\", value_name=\"accuracy\"\n", + ")\n", + "# Drop TF USEM + BM25 variant for clarity\n", + "melted = melted[melted[\"index\"] != \"TensorFlow USEM + BM25\"]\n", + "\n", + "sns.barplot(\n", + " data=melted.sort_values(by=\"accuracy\", ascending=False),\n", + " x=\"k\",\n", + " y=\"accuracy\",\n", + " hue=\"index\",\n", + " ax=ax,\n", + ")\n", + "\n", + "ax.set_title(\"Top-k Accuracy Comparison Across Models\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Accuracy\")\n", + "ax.set_ylim(0, 1)\n", + "ax.legend(title=\"Model\", bbox_to_anchor=(1.05, 1), loc=\"upper left\")\n", + "ax.grid(axis=\"y\", alpha=0.3)\n", + "plt.xticks(rotation=0)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ff3390f", + "metadata": {}, + "outputs": [], + "source": [ + "# Per-label comparison for top-1 accuracy\n", + "label_index = [l for l in baseline_accuracy_pivot.index if l != \"OVERALL\"]\n", + "label_comparison = pd.DataFrame(\n", + " {\n", + " \"TensorFlow USEM\": baseline_accuracy_pivot.loc[label_index, 1],\n", + " \"DistilUSE\": distiluse_accuracy_pivot.loc[label_index, 1],\n", + " \"MiniLM\": minilm_accuracy_pivot.loc[label_index, 1],\n", + " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"].loc[\n", + " label_index, 1\n", + " ],\n", + " \"DistilUSE + BM25\": distiluse_hybrid_pivot.loc[label_index, 1],\n", + " \"MiniLM + BM25\": minilm_hybrid_pivot.loc[label_index, 1],\n", + " }\n", + ")\n", + "\n", + "label_comparison" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34902f2c", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize comparison across labels as bar chart, sorted by performance at each label\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "\n", + "melted = label_comparison.reset_index().melt(\n", + " id_vars=\"label\", var_name=\"Model\", value_name=\"accuracy\"\n", + ")\n", + "\n", + "sns.barplot(\n", + " data=melted.sort_values(by=[\"label\", \"accuracy\"], ascending=False),\n", + " x=\"label\",\n", + " y=\"accuracy\",\n", + " hue=\"Model\",\n", + " ax=ax,\n", + ")\n", + "\n", + "ax.set_title(\"Top-1 Accuracy Comparison by Label\")\n", + "ax.set_xlabel(\"Label\")\n", + "ax.set_ylabel(\"Accuracy\")\n", + "ax.set_ylim(0, 1)\n", + "ax.legend(title=\"Model\", bbox_to_anchor=(1.05, 1), loc=\"upper left\")\n", + "ax.grid(axis=\"y\", alpha=0.3)\n", + "plt.xticks(rotation=45, ha=\"right\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "149f195b", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize comparison across labels as bar chart, sorted by performance at each label\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "\n", + "melted = label_comparison.reset_index().melt(\n", + " id_vars=\"label\", var_name=\"Model\", value_name=\"accuracy\"\n", + ")\n", + "\n", + "for label in melted[\"label\"].unique():\n", + " subset = melted[melted[\"label\"] == label].sort_values(\n", + " by=\"accuracy\", ascending=False\n", + " )\n", + " sns.barplot(\n", + " data=subset,\n", + " x=\"label\",\n", + " y=\"accuracy\",\n", + " hue=\"Model\",\n", + " ax=ax,\n", + " )\n", + "\n", + "# Deduplicate legend entries and draw once after plotting\n", + "handles, labels = ax.get_legend_handles_labels()\n", + "by_label = dict(zip(labels, handles))\n", + "ax.legend(\n", + " by_label.values(),\n", + " by_label.keys(),\n", + " title=\"Model\",\n", + " bbox_to_anchor=(1.05, 1),\n", + " loc=\"upper left\",\n", + ")\n", + "\n", + "ax.set_title(\"Top-1 Accuracy Comparison by Label\")\n", + "ax.set_xlabel(\"Label\")\n", + "ax.set_ylabel(\"Accuracy\")\n", + "ax.set_ylim(0, 1)\n", + "ax.grid(axis=\"y\", alpha=0.3)\n", + "plt.xticks(rotation=45, ha=\"right\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3af7d0a0", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize comparison across labels as bar chart, sorted by performance at each label\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "\n", + "melted = label_comparison.reset_index().melt(\n", + " id_vars=\"label\", var_name=\"Model\", value_name=\"accuracy\"\n", + ")\n", + "\n", + "# Drop TF USEM + BM25 variant for clarity\n", + "melted = melted[melted[\"Model\"] != \"TensorFlow USEM + BM25\"]\n", + "\n", + "for label in melted[\"label\"].unique():\n", + " subset = melted[melted[\"label\"] == label].sort_values(\n", + " by=\"accuracy\", ascending=False\n", + " )\n", + " sns.barplot(\n", + " data=subset,\n", + " x=\"label\",\n", + " y=\"accuracy\",\n", + " hue=\"Model\",\n", + " ax=ax,\n", + " )\n", + "\n", + "# Deduplicate legend entries and draw once after plotting\n", + "handles, labels = ax.get_legend_handles_labels()\n", + "by_label = dict(zip(labels, handles))\n", + "ax.legend(\n", + " by_label.values(),\n", + " by_label.keys(),\n", + " title=\"Model\",\n", + " bbox_to_anchor=(1.05, 1),\n", + " loc=\"upper left\",\n", + ")\n", + "\n", + "ax.set_title(\"Top-1 Accuracy Comparison by Label\")\n", + "ax.set_xlabel(\"Label\")\n", + "ax.set_ylabel(\"Accuracy\")\n", + "ax.set_ylim(0, 1)\n", + "ax.grid(axis=\"y\", alpha=0.3)\n", + "plt.xticks(rotation=45, ha=\"right\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9aefccac", + "metadata": {}, + "outputs": [], + "source": [ + "# Determine champion model\n", + "top1_scores = comparison_df[1]\n", + "champion_model = top1_scores.idxmax()\n", + "champion_score = top1_scores.max()\n", + "\n", + "# Calculate performance delta vs baseline\n", + "baseline_score = top1_scores[\"TensorFlow USEM\"]\n", + "challengers = top1_scores.drop(\"TensorFlow USEM\")\n", + "\n", + "performance_summary = pd.DataFrame(\n", + " {\n", + " \"Model\": comparison_df.index,\n", + " \"Top-1\": comparison_df[1].values,\n", + " \"Top-3\": comparison_df[3].values,\n", + " \"Top-5\": comparison_df[5].values,\n", + " \"Δ vs Baseline (Top-1)\": [\n", + " 0.0 if model == \"TensorFlow USEM\" else (score - baseline_score)\n", + " for model, score in zip(\n", + " comparison_df.index, comparison_df[1].values\n", + " )\n", + " ],\n", + " }\n", + ").set_index(\"Model\")\n", + "\n", + "performance_summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4a72754", + "metadata": {}, + "outputs": [], + "source": [ + "final_summary_md = f\"\"\"\n", + "## Champion Model Selection\n", + "\n", + "### Performance Summary\n", + "\n", + "{performance_summary.to_markdown()}\n", + "\n", + "### Winner: **{champion_model}**\n", + "\n", + "- **Top-1 Accuracy**: {champion_score:.2%}\n", + "- **Performance vs Baseline**: {(champion_score - baseline_score):.2%} ({\"+\" if champion_score >= baseline_score else \"\"}{((champion_score - baseline_score) / baseline_score * 100):.1f}%)\n", + "\n", + "### Key Findings\n", + "\n", + "1. **{champion_model}** achieves the highest top-1 accuracy at {champion_score:.2%}\n", + "2. All PyTorch models eliminate TensorFlow dependency and support Apple Silicon (MPS)\n", + "3. Both challenger models maintain competitive accuracy while offering:\n", + " - Cross-platform compatibility (Linux, macOS, Windows)\n", + " - Apple Silicon acceleration via MPS backend\n", + " - Smaller model footprint and faster inference\n", + "\n", + "### Implementation Recommendations\n", + "\n", + "1. **Replace TensorFlow USEM** with **{champion_model}** in production pipeline\n", + "2. Update `USEMSubcategorizer` to use the selected PyTorch encoder by default\n", + "3. Regenerate cached embeddings using the champion encoder\n", + "4. Update deployment documentation to reflect new dependencies\n", + "5. Verify inference latency and memory footprint in production environment\n", + "\n", + "### Next Steps\n", + "\n", + "- [ ] Implement {champion_model} as default encoder in `aymurai.models.usem`\n", + "- [ ] Regenerate response embeddings for all label categories\n", + "- [ ] Update pipeline configuration files\n", + "- [ ] Run integration tests across all supported platforms\n", + "- [ ] Update deployment scripts and documentation\n", + "\"\"\"\n", + "\n", + "Markdown(final_summary_md)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64638e96", + "metadata": {}, + "outputs": [], + "source": [ + "# Best-performing model per label and k (excluding TensorFlow USEM + BM25)\n", + "label_index = [l for l in baseline_accuracy_pivot.index if l != \"OVERALL\"]\n", + "ks = [k for k in range(1, 6) if k in baseline_accuracy_pivot.columns]\n", + "\n", + "model_pivots = {\n", + " \"TensorFlow USEM\": baseline_accuracy_pivot,\n", + " \"DistilUSE\": distiluse_accuracy_pivot,\n", + " \"MiniLM\": minilm_accuracy_pivot,\n", + " \"DistilUSE + BM25\": distiluse_hybrid_pivot,\n", + " \"MiniLM + BM25\": minilm_hybrid_pivot,\n", + "}\n", + "\n", + "records = []\n", + "for model_name, pivot in model_pivots.items():\n", + " for label in label_index:\n", + " for k in ks:\n", + " records.append(\n", + " {\n", + " \"label\": label,\n", + " \"k\": k,\n", + " \"model\": model_name,\n", + " \"accuracy\": pivot.loc[label, k],\n", + " }\n", + " )\n", + "\n", + "scores = pd.DataFrame(records)\n", + "best_models = scores.loc[scores.groupby([\"label\", \"k\"])[\"accuracy\"].idxmax()]\n", + "model_ranking = (\n", + " best_models[\"model\"]\n", + " .value_counts()\n", + " .rename_axis(\"model\")\n", + " .reset_index(name=\"wins\")\n", + ")\n", + "\n", + "best_models.reset_index(drop=True)\n", + "model_ranking" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fd27e32", + "metadata": {}, + "outputs": [], + "source": [ + "scores.query(\"k == 1\").sort_values(\n", + " [\"label\", \"accuracy\"], ascending=[True, False]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06b5a254", + "metadata": {}, + "outputs": [], + "source": [ + "scores.query(\"k == 3\").sort_values(\n", + " [\"label\", \"accuracy\"], ascending=[True, False]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b20b3ac", + "metadata": {}, + "outputs": [], + "source": [ + "scores.query(\"k == 5\").sort_values(\n", + " [\"label\", \"accuracy\"], ascending=[True, False]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "977f446a", + "metadata": {}, + "outputs": [], + "source": [ + "# Grid search BM25 weights for DistilUSE and MiniLM hybrids\n", + "weight_grid = [round(w, 2) for w in np.linspace(0.0, 0.9, 10)]\n", + "weight_grid" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11f9300a", + "metadata": {}, + "outputs": [], + "source": [ + "search_results = []\n", + "pivot_lookup = {}\n", + "\n", + "# Cache texts per label\n", + "label_texts = {\n", + " label: [item[\"text\"] for item in items]\n", + " for label, items in samples_by_label.items()\n", + "}\n", + "\n", + "# Cache BM25 score vectors per label to avoid recomputing across weights\n", + "bm25_cache = {\n", + " label: [bm25_models[label].score_vector(text) for text in texts]\n", + " for label, texts in label_texts.items()\n", + "}\n", + "\n", + "# Cache question embeddings per model and label\n", + "query_cache = {}\n", + "for model_name, base_models in {\n", + " \"DistilUSE\": distiluse_models,\n", + " \"MiniLM\": minilm_models,\n", + "}.items():\n", + " any_model = next(iter(base_models.values()))\n", + " encoder = any_model.encoder\n", + " batch_size = any_model.batch_size\n", + " query_cache[model_name] = {\n", + " label: encoder.batch_encode(\n", + " texts, encoder_type=\"question_encoder\", batch_size=batch_size\n", + " )\n", + " for label, texts in label_texts.items()\n", + " }\n", + "\n", + "\n", + "def hybrid_retrieve_cached(\n", + " base_retriever,\n", + " bm25_model,\n", + " texts,\n", + " query_vectors,\n", + " bm25_scores,\n", + " bm25_weight,\n", + " top_k=5,\n", + "):\n", + " similarity = np.inner(query_vectors, base_retriever.response_vectors)\n", + " combined_results = []\n", + " for idx, text in enumerate(texts):\n", + " bm25_vec = bm25_scores[idx]\n", + " bm25_norm = bm25_vec / (bm25_vec.max() + 1e-9)\n", + " sim_scores = similarity[idx]\n", + " sim_norm = sim_scores / (sim_scores.max() + 1e-9)\n", + " combined = bm25_weight * bm25_norm + (1 - bm25_weight) * sim_norm\n", + " k = min(top_k, combined.shape[0])\n", + " top_indices = np.argsort(-combined)[:k]\n", + " combined_results.append(\n", + " [bm25_model.subcategories[i] for i in top_indices]\n", + " )\n", + " return combined_results\n", + "\n", + "\n", + "for model_name, base_models in {\n", + " \"DistilUSE\": distiluse_models,\n", + " \"MiniLM\": minilm_models,\n", + "}.items():\n", + " for weight in weight_grid:\n", + " records = []\n", + " for label, base_retriever in base_models.items():\n", + " texts = label_texts[label]\n", + " query_vectors = query_cache[model_name][label]\n", + " bm25_scores = bm25_cache[label]\n", + " retrieved_lists = hybrid_retrieve_cached(\n", + " base_retriever=base_retriever,\n", + " bm25_model=bm25_models[label],\n", + " texts=texts,\n", + " query_vectors=query_vectors,\n", + " bm25_scores=bm25_scores,\n", + " bm25_weight=weight,\n", + " top_k=5,\n", + " )\n", + " for item, retrieved in zip(\n", + " samples_by_label[label], retrieved_lists\n", + " ):\n", + " records.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"retrieved\": retrieved,\n", + " }\n", + " )\n", + " accuracy = compute_topk_accuracy(records)\n", + " pivot = accuracy.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + " ).sort_index()\n", + " pivot_lookup[(model_name, weight)] = pivot\n", + " overall = pivot.loc[\"OVERALL\"]\n", + " search_results.append(\n", + " {\n", + " \"model\": model_name,\n", + " \"bm25_weight\": weight,\n", + " \"top1\": overall.get(1, np.nan),\n", + " \"top3\": overall.get(3, np.nan),\n", + " \"top5\": overall.get(5, np.nan),\n", + " }\n", + " )\n", + "\n", + "search_df = pd.DataFrame(search_results)\n", + "\n", + "# Best weight per model by top-1 accuracy\n", + "best_rows = search_df.loc[\n", + " search_df.groupby(\"model\")[\"top1\"].idxmax()\n", + "].reset_index(drop=True)\n", + "best_pivots = {\n", + " row.model: pivot_lookup[(row.model, row.bm25_weight)]\n", + " for _, row in best_rows.iterrows()\n", + "}\n", + "\n", + "best_rows" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3ade77d", + "metadata": {}, + "outputs": [], + "source": [ + "search_df" + ] + }, + { + "cell_type": "markdown", + "id": "23d4f97e", + "metadata": {}, + "source": [ + "### Hybrid (tuned BM25 + encoder) evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7323a3c", + "metadata": {}, + "outputs": [], + "source": [ + "# Evaluate hybrids at tuned BM25 weights\n", + "\n", + "tuned_records = {}\n", + "tuned_pivots = {}\n", + "\n", + "for row in best_rows.itertuples():\n", + " model_name = row.model\n", + " weight = row.bm25_weight\n", + " base_models = (\n", + " distiluse_models if model_name == \"DistilUSE\" else minilm_models\n", + " )\n", + "\n", + " records = []\n", + " for label, base_retriever in base_models.items():\n", + " texts = label_texts[label]\n", + " query_vectors = query_cache[model_name][label]\n", + " bm25_scores = bm25_cache[label]\n", + " retrieved_lists = hybrid_retrieve_cached(\n", + " base_retriever=base_retriever,\n", + " bm25_model=bm25_models[label],\n", + " texts=texts,\n", + " query_vectors=query_vectors,\n", + " bm25_scores=bm25_scores,\n", + " bm25_weight=weight,\n", + " top_k=5,\n", + " )\n", + " for item, retrieved in zip(samples_by_label[label], retrieved_lists):\n", + " records.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"retrieved\": retrieved,\n", + " }\n", + " )\n", + "\n", + " accuracy = compute_topk_accuracy(records)\n", + " pivot = accuracy.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + " ).sort_index()\n", + " tuned_records[model_name] = records\n", + " tuned_pivots[model_name] = pivot\n", + "\n", + "# Show tuned weights and overall performance\n", + "best_rows_display = best_rows.copy()\n", + "for model_name, pivot in tuned_pivots.items():\n", + " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top1\"] = (\n", + " pivot.loc[\"OVERALL\", 1]\n", + " )\n", + " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top3\"] = (\n", + " pivot.loc[\"OVERALL\", 3]\n", + " )\n", + " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top5\"] = (\n", + " pivot.loc[\"OVERALL\", 5]\n", + " )\n", + "\n", + "best_rows_display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86348779", + "metadata": {}, + "outputs": [], + "source": [ + "# Full pipeline re-evaluation with evaluate_sentence_transformers at tuned weights\n", + "\n", + "best_weights = best_rows.set_index(\"model\")[\"bm25_weight\"].to_dict()\n", + "\n", + "# Build tuned hybrid retrievers (standard pipeline, no caching shortcuts)\n", + "tuned_hybrid_models = {\n", + " \"DistilUSE + BM25 tuned\": build_hybrid_sentence_transformer_models(\n", + " distiluse_models,\n", + " bm25_models,\n", + " bm25_weight=float(best_weights[\"DistilUSE\"]),\n", + " ),\n", + " \"MiniLM + BM25 tuned\": build_hybrid_sentence_transformer_models(\n", + " minilm_models, bm25_models, bm25_weight=float(best_weights[\"MiniLM\"])\n", + " ),\n", + "}\n", + "\n", + "# Run evaluation pipeline\n", + "attribution = {}\n", + "tuned_hybrid_records = {}\n", + "tuned_hybrid_accuracy = {}\n", + "tuned_hybrid_pivots = {}\n", + "\n", + "for name, models in tuned_hybrid_models.items():\n", + " records = evaluate_sentence_transformers(samples_by_label, models, top_k=5)\n", + " tuned_hybrid_records[name] = records\n", + " df = compute_topk_accuracy(records)\n", + " tuned_hybrid_accuracy[name] = df\n", + " tuned_hybrid_pivots[name] = df.pivot(\n", + " index=\"label\", columns=\"k\", values=\"accuracy\"\n", + " ).sort_index()\n", + "\n", + "# Overall comparison table including tuned hybrids\n", + "comparison_df_tuned = pd.DataFrame(\n", + " {\n", + " \"TensorFlow USEM\": baseline_accuracy_pivot.loc[\"OVERALL\"],\n", + " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"].loc[\n", + " \"OVERALL\"\n", + " ],\n", + " \"DistilUSE\": distiluse_accuracy_pivot.loc[\"OVERALL\"],\n", + " \"MiniLM\": minilm_accuracy_pivot.loc[\"OVERALL\"],\n", + " \"DistilUSE + BM25 tuned\": tuned_hybrid_pivots[\n", + " \"DistilUSE + BM25 tuned\"\n", + " ].loc[\"OVERALL\"],\n", + " \"MiniLM + BM25 tuned\": tuned_hybrid_pivots[\"MiniLM + BM25 tuned\"].loc[\n", + " \"OVERALL\"\n", + " ],\n", + " }\n", + ").T\n", + "\n", + "comparison_df_tuned.sort_values(by=1, ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8bdb7199", + "metadata": {}, + "outputs": [], + "source": [ + "# Heatmaps per label for each model\n", + "pivot_map = {\n", + " \"TensorFlow USEM\": baseline_accuracy_pivot,\n", + " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"],\n", + " \"DistilUSE\": distiluse_accuracy_pivot,\n", + " \"MiniLM\": minilm_accuracy_pivot,\n", + " \"DistilUSE + BM25 tuned\": tuned_hybrid_pivots[\"DistilUSE + BM25 tuned\"],\n", + " \"MiniLM + BM25 tuned\": tuned_hybrid_pivots[\"MiniLM + BM25 tuned\"],\n", + "}\n", + "\n", + "labels_no_overall = [l for l in label_index]\n", + "\n", + "for name, pivot in pivot_map.items():\n", + " fig, ax = plt.subplots(figsize=(6, 4))\n", + " sns.heatmap(\n", + " pivot.loc[labels_no_overall],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " vmin=0,\n", + " vmax=1,\n", + " ax=ax,\n", + " )\n", + " ax.set_title(f\"{name} top-k accuracy by label\")\n", + " ax.set_xlabel(\"k\")\n", + " ax.set_ylabel(\"Label\")\n", + " plt.tight_layout()\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "961d15ad", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/sentence-encoders/03-sentence-transformer-subcategorizer-smoke.ipynb b/notebooks/experiments/sentence-encoders/03-sentence-transformer-subcategorizer-smoke.ipynb new file mode 100644 index 00000000..8b84293e --- /dev/null +++ b/notebooks/experiments/sentence-encoders/03-sentence-transformer-subcategorizer-smoke.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "0364d1dd", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ab7b227", + "metadata": {}, + "outputs": [], + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "\n", + "from aymurai.models.sentence_encoder.base import BaseSentenceEncoder\n", + "from aymurai.transforms.entity_subcategories.sentence_transformer import (\n", + " SentenceTransformerSubcategorizer,\n", + ")\n", + "\n", + "\n", + "class FakeEncoder(BaseSentenceEncoder):\n", + " def _vec(self, text: str) -> np.ndarray:\n", + " # Deterministic 8-dim vector from hash\n", + " h = abs(hash(text))\n", + " return np.array([(h >> i) & 0xFF for i in range(0, 64, 8)], dtype=np.float32)\n", + "\n", + " def encode(self, text_array, encoder_type, context_array=None):\n", + " return self.batch_encode(text_array, encoder_type)\n", + "\n", + " def batch_encode(self, text_array, encoder_type, batch_size: int = 256):\n", + " texts = list(text_array)\n", + " return np.vstack([self._vec(self.normalize_text(t)) for t in texts])\n", + "\n", + "\n", + "tmpdir = Path(tempfile.mkdtemp())\n", + "subcats_path = tmpdir / \"subcats.txt\"\n", + "subcats_path.write_text(\"\\n\".join([\"alpha\", \"beta\", \"gamma\"]))\n", + "\n", + "# BM25 + encoder path\n", + "hybrid_embeddings = tmpdir / \"embeddings_hybrid.npz\"\n", + "hybrid = SentenceTransformerSubcategorizer(\n", + " category=\"CONDUCTA\",\n", + " embeddings_path=str(hybrid_embeddings),\n", + " encoder_name=\"distiluse\",\n", + " bm25_weight=0.5,\n", + " encoder=FakeEncoder(),\n", + ")\n", + "print(hybrid.batch_retrieve([\"alpha case\", \"beta story\"], top_k=2))\n", + "\n", + "# Encoder-only path (bm25_weight = 0)\n", + "encoder_only_embeddings = tmpdir / \"embeddings_encoder_only.npz\"\n", + "encoder_only = SentenceTransformerSubcategorizer(\n", + " category=\"CONDUCTA\",\n", + " embeddings_path=str(encoder_only_embeddings),\n", + " encoder_name=\"distiluse\",\n", + " bm25_weight=0.0,\n", + " encoder=FakeEncoder(),\n", + " rebuild_embeddings=True,\n", + ")\n", + "print(encoder_only.batch_retrieve([\"gamma topic\"], top_k=2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c3bf286", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb b/notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb new file mode 100644 index 00000000..83546815 --- /dev/null +++ b/notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb @@ -0,0 +1,420 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "7071ca80", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aacdfd29", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "from collections import defaultdict\n", + "from pathlib import Path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "\n", + "from aymurai.transforms.entity_subcategories.sentence_transformer import (\n", + " SentenceTransformerSubcategorizer,\n", + ")\n", + "from aymurai.utils.json_data import load_json\n", + "\n", + "sns.set_theme(style=\"whitegrid\")\n", + "plt.rcParams.update(\n", + " {\"figure.figsize\": (10, 6), \"axes.titlesize\": 14, \"axes.labelsize\": 12}\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b102ccf", + "metadata": {}, + "outputs": [], + "source": [ + "# Config\n", + "pipeline_path = Path(\"/resources/pipelines/production/full-paragraph/pipeline.json\")\n", + "annotations_path = Path(\n", + " \"/resources/annotations/label-studio/resos-annotations/30-nov/project-3-at-2022-11-30-16-04-2b43bf39.json\"\n", + ")\n", + "target_labels = [\n", + " \"CONDUCTA\",\n", + " \"CONDUCTA_DESCRIPCION\",\n", + " \"DETALLE\",\n", + " \"OBJETO_DE_LA_RESOLUCION\",\n", + "]\n", + "encoder_name = \"distiluse\"\n", + "bm25_weight = 0.5 # use tuned default; set 0.0 for encoder-only\n", + "batch_size = 256\n", + "embeddings_dir = Path(\"/resources/cache/aymurai\")\n", + "embeddings_dir.mkdir(parents=True, exist_ok=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe9a836a", + "metadata": {}, + "outputs": [], + "source": [ + "def collect_label_annotations(annotations, target_labels):\n", + " buckets_by_label = defaultdict(list)\n", + " target_labels = set(target_labels)\n", + "\n", + " for task in annotations:\n", + " for annotation in task.get(\"annotations\", []):\n", + " merged = {}\n", + " for result in annotation.get(\"result\", []):\n", + " result_id = result.get(\"id\")\n", + " value = result.get(\"value\", {})\n", + " slot = merged.setdefault(\n", + " result_id,\n", + " {\n", + " \"text\": value.get(\"text\"),\n", + " \"start\": value.get(\"start\"),\n", + " \"end\": value.get(\"end\"),\n", + " \"labels\": [],\n", + " \"choices\": [],\n", + " },\n", + " )\n", + " slot[\"text\"] = slot[\"text\"] or value.get(\"text\")\n", + " slot[\"start\"] = (\n", + " slot[\"start\"] if slot[\"start\"] is not None else value.get(\"start\")\n", + " )\n", + " slot[\"end\"] = (\n", + " slot[\"end\"] if slot[\"end\"] is not None else value.get(\"end\")\n", + " )\n", + " slot[\"labels\"].extend(value.get(\"labels\", []))\n", + " slot[\"choices\"].extend(value.get(\"choices\", []))\n", + "\n", + " for payload in merged.values():\n", + " labels = [\n", + " label for label in payload[\"labels\"] if label in target_labels\n", + " ]\n", + " if not labels or payload[\"text\"] is None:\n", + " continue\n", + " item = {\n", + " \"text\": payload[\"text\"],\n", + " \"labels\": list(dict.fromkeys(payload[\"labels\"])),\n", + " \"choices\": list(dict.fromkeys(payload[\"choices\"])),\n", + " \"start\": payload[\"start\"],\n", + " \"end\": payload[\"end\"],\n", + " }\n", + " for label in labels:\n", + " buckets_by_label[label].append(item)\n", + " return {label: buckets_by_label.get(label, []) for label in target_labels}\n", + "\n", + "\n", + "def compute_topk_accuracy(records, ks=(1, 2, 3, 4, 5)):\n", + " metrics = []\n", + " labels = sorted({record[\"label\"] for record in records})\n", + " for label in labels:\n", + " label_records = [r for r in records if r[\"label\"] == label]\n", + " total = len(label_records)\n", + " for k in ks:\n", + " hits = sum(\n", + " any(choice in r[\"retrieved\"][:k] for choice in r[\"choices\"])\n", + " for r in label_records\n", + " )\n", + " metrics.append(\n", + " {\"label\": label, \"k\": k, \"accuracy\": hits / total if total else np.nan}\n", + " )\n", + " overall = []\n", + " for k in ks:\n", + " hits = sum(\n", + " any(choice in r[\"retrieved\"][:k] for choice in r[\"choices\"])\n", + " for r in records\n", + " )\n", + " overall.append(\n", + " {\n", + " \"label\": \"OVERALL\",\n", + " \"k\": k,\n", + " \"accuracy\": hits / len(records) if records else np.nan,\n", + " }\n", + " )\n", + " metrics.extend(overall)\n", + " return pd.DataFrame(metrics)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64556a79", + "metadata": {}, + "outputs": [], + "source": [ + "# Load annotations and collect samples\n", + "annotations = load_json(str(annotations_path))\n", + "samples_by_label = collect_label_annotations(annotations, target_labels)\n", + "# filter out empty-choice samples\n", + "for label in list(samples_by_label):\n", + " samples_by_label[label] = [s for s in samples_by_label[label] if s.get(\"choices\")]\n", + "\n", + "\n", + "def samples_to_dataframe(samples):\n", + " rows = []\n", + " for label, items in samples.items():\n", + " for item in items:\n", + " rows.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"n_choices\": len(item[\"choices\"]),\n", + " \"char_len\": len(item[\"text\"]),\n", + " }\n", + " )\n", + " return pd.DataFrame(rows)\n", + "\n", + "\n", + "samples_df = samples_to_dataframe(samples_by_label)\n", + "samples_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a93a2087", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract sentence-transformer config from pipeline (or fall back to targets)\n", + "with pipeline_path.open() as f:\n", + " pipeline_config = json.load(f)\n", + "\n", + "st_configs = {\n", + " config[1][\"category\"]: {\n", + " \"embeddings_path\": config[1].get(\"embeddings_path\"),\n", + " }\n", + " for config in pipeline_config.get(\"postprocess\", [])\n", + " if \"SentenceTransformerSubcategorizer\" in config[0]\n", + "}\n", + "\n", + "# Fallback: build configs from target_labels when pipeline does not define them\n", + "if not st_configs:\n", + " st_configs = {\n", + " label: {\"embeddings_path\": embeddings_dir / f\"{label.lower()}.npz\"}\n", + " for label in target_labels\n", + " }\n", + "\n", + "st_configs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fdf84cf", + "metadata": {}, + "outputs": [], + "source": [ + "# Build sentence-transformer subcategorizer models (hybrid by default)\n", + "models = {}\n", + "for label, cfg in st_configs.items():\n", + " emb_path = cfg.get(\"embeddings_path\") or (embeddings_dir / f\"{label.lower()}.npz\")\n", + " models[label] = SentenceTransformerSubcategorizer(\n", + " category=label,\n", + " embeddings_path=str(emb_path),\n", + " encoder_name=encoder_name,\n", + " bm25_weight=bm25_weight,\n", + " batch_size=batch_size,\n", + " rebuild_embeddings=True,\n", + " )\n", + "list(models.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e51e5b92", + "metadata": {}, + "outputs": [], + "source": [ + "# Re run to check loading existing embeddings\n", + "models = {}\n", + "for label, cfg in st_configs.items():\n", + " emb_path = cfg.get(\"embeddings_path\") or (embeddings_dir / f\"{label.lower()}.npz\")\n", + " models[label] = SentenceTransformerSubcategorizer(\n", + " category=label,\n", + " embeddings_path=str(emb_path),\n", + " encoder_name=encoder_name,\n", + " bm25_weight=bm25_weight,\n", + " batch_size=batch_size,\n", + " rebuild_embeddings=False,\n", + " )\n", + "list(models.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a384a261", + "metadata": {}, + "outputs": [], + "source": [ + "# Evaluate\n", + "records = []\n", + "for label, items in samples_by_label.items():\n", + " retriever = models[label]\n", + " texts = [item[\"text\"] for item in items]\n", + " retrieved_lists = retriever.batch_retrieve(texts, top_k=5)\n", + " for item, retrieved in zip(items, retrieved_lists):\n", + " records.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"retrieved\": retrieved,\n", + " }\n", + " )\n", + "\n", + "accuracy_df = compute_topk_accuracy(records)\n", + "pivot = accuracy_df.pivot(index=\"label\", columns=\"k\", values=\"accuracy\").sort_index()\n", + "pivot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdc73175", + "metadata": {}, + "outputs": [], + "source": [ + "# Heatmap\n", + "fig, ax = plt.subplots()\n", + "sns.heatmap(\n", + " pivot.loc[[l for l in pivot.index if l != \"OVERALL\"]],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"SentenceTransformerSubcategorizer (distiluse) top-k accuracy\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "pivot.loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a99ad20", + "metadata": {}, + "outputs": [], + "source": [ + "encoder_name = \"minilm\"\n", + "\n", + "# Build sentence-transformer subcategorizer models (hybrid by default)\n", + "models = {}\n", + "for label, cfg in st_configs.items():\n", + " emb_path = cfg.get(\"embeddings_path\") or (embeddings_dir / f\"{label.lower()}.npz\")\n", + " models[label] = SentenceTransformerSubcategorizer(\n", + " category=label,\n", + " embeddings_path=str(emb_path),\n", + " encoder_name=encoder_name,\n", + " bm25_weight=bm25_weight,\n", + " batch_size=batch_size,\n", + " rebuild_embeddings=True,\n", + " )\n", + "list(models.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5c8bf7e", + "metadata": {}, + "outputs": [], + "source": [ + "# Evaluate\n", + "records = []\n", + "for label, items in samples_by_label.items():\n", + " retriever = models[label]\n", + " texts = [item[\"text\"] for item in items]\n", + " retrieved_lists = retriever.batch_retrieve(texts, top_k=5)\n", + " for item, retrieved in zip(items, retrieved_lists):\n", + " records.append(\n", + " {\n", + " \"label\": label,\n", + " \"text\": item[\"text\"],\n", + " \"choices\": item[\"choices\"],\n", + " \"retrieved\": retrieved,\n", + " }\n", + " )\n", + "\n", + "accuracy_df = compute_topk_accuracy(records)\n", + "pivot = accuracy_df.pivot(index=\"label\", columns=\"k\", values=\"accuracy\").sort_index()\n", + "pivot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e3700072", + "metadata": {}, + "outputs": [], + "source": [ + "# Heatmap\n", + "fig, ax = plt.subplots()\n", + "sns.heatmap(\n", + " pivot.loc[[l for l in pivot.index if l != \"OVERALL\"]],\n", + " annot=True,\n", + " fmt=\".2f\",\n", + " cmap=\"YlGnBu\",\n", + " ax=ax,\n", + ")\n", + "ax.set_title(\"SentenceTransformerSubcategorizer (minilm) top-k accuracy\")\n", + "ax.set_xlabel(\"k\")\n", + "ax.set_ylabel(\"Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "pivot.loc[[\"OVERALL\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9c46485", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/subcategories/00-exploration.ipynb b/notebooks/experiments/subcategories/00-exploration.ipynb index f29849cc..348323b0 100644 --- a/notebooks/experiments/subcategories/00-exploration.ipynb +++ b/notebooks/experiments/subcategories/00-exploration.ipynb @@ -31,6 +31,7 @@ "outputs": [], "source": [ "import os\n", + "\n", "os.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"" ] }, @@ -77,13 +78,13 @@ "metadata": {}, "outputs": [], "source": [ - "from aymurai.spacy.display import DocRender\n", - "from aymurai.datasets.ar_juz_pcyf_10.docs import ArgentinaJuzgadoPCyF10DocsDataset\n", + "from aymurai.datasets.ar_juz_pcyf_10.docs import (\n", + " ArgentinaJuzgadoPCyF10DocsDataset,\n", + ")\n", "from aymurai.datasets.ar_juz_pcyf_10.annotations import (\n", " ArgentinaJuzgadoPCyF10LabelStudioAnnotations,\n", ")\n", "\n", - "render = DocRender()\n", "docs = ArgentinaJuzgadoPCyF10LabelStudioAnnotations(\n", " \"/resources/data/restricted/annotations/20221130-bis/\"\n", ").data\n", @@ -772,13 +773,13 @@ "metadata": {}, "outputs": [], "source": [ - "with open('./conduct_options.txt', 'r') as file:\n", + "with open(\"./conduct_options.txt\", \"r\") as file:\n", " conduct_categories = file.read().splitlines()\n", - "with open('./conduct_desc_options.txt', 'r') as file:\n", + "with open(\"./conduct_desc_options.txt\", \"r\") as file:\n", " conduct_desc_categories = file.read().splitlines()\n", - "with open('./detail_options.txt', 'r') as file:\n", + "with open(\"./detail_options.txt\", \"r\") as file:\n", " detail_categories = file.read().splitlines()\n", - "with open('./object_options.txt', 'r') as file:\n", + "with open(\"./object_options.txt\", \"r\") as file:\n", " object_categories = file.read().splitlines()" ] }, @@ -801,7 +802,10 @@ "from aymurai.models.flair.core import FlairModel\n", "from aymurai.models.flair.utils import FlairTextNormalize\n", "from aymurai.transforms.entity_subcategories.regex import RegexSubcategorizer\n", - "from aymurai.transforms.entity_subcategories.usem import USEMSubcategorizePipeline, USEMSubcategorizer\n", + "from aymurai.transforms.entity_subcategories.usem import (\n", + " USEMSubcategorizePipeline,\n", + " USEMSubcategorizer,\n", + ")\n", "\n", "config = {\n", " \"preprocess\": [\n", @@ -821,39 +825,39 @@ " (RegexSubcategorizer, {}),\n", " (\n", " USEMSubcategorizer,\n", - " {\n", - " \"category\": \"CONDUCTA\",\n", - " # \"subcategories\": conduct_categories,\n", - " \"subcategories_path\": './conduct_options.txt',\n", - " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/conduct_embeddings.npy\",\n", - " }\n", + " {\n", + " \"category\": \"CONDUCTA\",\n", + " # \"subcategories\": conduct_categories,\n", + " \"subcategories_path\": \"./conduct_options.txt\",\n", + " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/conduct_embeddings.npy\",\n", + " },\n", " ),\n", " (\n", " USEMSubcategorizer,\n", - " {\n", - " \"category\": \"CONDUCTA_DESCRIPCION\",\n", - " # \"subcategories\": conduct_descr_categories,\n", - " \"subcategories_path\": './conduct_desc_options.txt',\n", - " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/conduct_descr_embeddings.npy\",\n", - " }\n", + " {\n", + " \"category\": \"CONDUCTA_DESCRIPCION\",\n", + " # \"subcategories\": conduct_descr_categories,\n", + " \"subcategories_path\": \"./conduct_desc_options.txt\",\n", + " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/conduct_descr_embeddings.npy\",\n", + " },\n", " ),\n", " (\n", " USEMSubcategorizer,\n", - " {\n", - " \"category\": \"DETALLE\",\n", - " # \"subcategories\": detail_categories,\n", - " \"subcategories_path\": './detail_options.txt',\n", - " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/detail_embeddings.npy\",\n", - " }\n", + " {\n", + " \"category\": \"DETALLE\",\n", + " # \"subcategories\": detail_categories,\n", + " \"subcategories_path\": \"./detail_options.txt\",\n", + " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/detail_embeddings.npy\",\n", + " },\n", " ),\n", " (\n", " USEMSubcategorizer,\n", - " {\n", - " \"category\": \"OBJETO_DE_LA_RESOLUCION\",\n", - " \"subcategories_path\": './object_options.txt',\n", - " # \"subcategories\": object_categories,\n", - " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/object_embeddings.npy\",\n", - " }\n", + " {\n", + " \"category\": \"OBJETO_DE_LA_RESOLUCION\",\n", + " \"subcategories_path\": \"./object_options.txt\",\n", + " # \"subcategories\": object_categories,\n", + " \"response_embeddings_path\": \"/resources/pipelines/examples/flair-simple/UsemEmbeddings/object_embeddings.npy\",\n", + " },\n", " ),\n", " ],\n", " # \"multiprocessing\": {},\n", @@ -871,7 +875,7 @@ "source": [ "pipeline = AymurAIPipeline(config)\n", "results = pipeline.preprocess(sample[:1])\n", - "results = pipeline.predict(results)\n" + "results = pipeline.predict(results)" ] }, { @@ -881,7 +885,6 @@ "metadata": {}, "outputs": [], "source": [ - "\n", "pp = pipeline.postprocess(results)" ] }, @@ -948,7 +951,7 @@ "# # \"multiprocessing\": {},\n", "# \"use_cache\": False,\n", "# # 'log_level': 'debug'\n", - "# }\n" + "# }" ] }, { @@ -959,7 +962,7 @@ "outputs": [], "source": [ "# pipeline = AymurAIPipeline(config)\n", - "# " + "#" ] }, { @@ -969,9 +972,8 @@ "metadata": {}, "outputs": [], "source": [ - "\n", "# preprocess = pipeline.preprocess(sample)\n", - "# results = pipeline.predict(preprocess)\n" + "# results = pipeline.predict(preprocess)" ] }, { @@ -981,7 +983,7 @@ "metadata": {}, "outputs": [], "source": [ - "# \n", + "#\n", "postprocess = pipeline.postprocess(results)" ] }, @@ -1029,7 +1031,11 @@ "outputs": [], "source": [ "for label in [\"VIOLENCIA_DE_GENERO\"]:\n", - " __ = [_ for _ in results[idx][\"predictions\"][\"entities\"] if _[\"label\"] == label]\n", + " __ = [\n", + " _\n", + " for _ in results[idx][\"predictions\"][\"entities\"]\n", + " if _[\"label\"] == label\n", + " ]\n", " print(label)\n", " display(__)\n", " print(\"\\n\\n\")" @@ -1044,8 +1050,17 @@ }, "outputs": [], "source": [ - "for label in [\"CONDUCTA\", \"CONDUCTA_DESCRIPCION\", \"DETALLE\", \"OBJETO_DE_LA_RESOLUCION\"]:\n", - " __ = [_ for _ in results[idx][\"predictions\"][\"entities\"] if _[\"label\"] == label]\n", + "for label in [\n", + " \"CONDUCTA\",\n", + " \"CONDUCTA_DESCRIPCION\",\n", + " \"DETALLE\",\n", + " \"OBJETO_DE_LA_RESOLUCION\",\n", + "]:\n", + " __ = [\n", + " _\n", + " for _ in results[idx][\"predictions\"][\"entities\"]\n", + " if _[\"label\"] == label\n", + " ]\n", " print(label)\n", " display(__)\n", " print(\"\\n\\n\")" @@ -1068,8 +1083,9 @@ "source": [ "import re\n", "import pandas as pd\n", + "\n", "pd.set_option(\"display.max_rows\", 500)\n", - "pd.set_option('max_colwidth', 400)" + "pd.set_option(\"max_colwidth\", 400)" ] }, { @@ -1080,7 +1096,7 @@ "outputs": [], "source": [ "path = \"/resources/ner/flair/resos-20221130-no-decision/\"\n", - "df = pd.read_csv(path+\"train.txt\", sep=\"\\s\", header=None)\n", + "df = pd.read_csv(path + \"train.txt\", sep=\"\\s\", header=None)\n", "df.columns = [\"token\", \"label\"]\n", "df.head()" ] @@ -1127,7 +1143,7 @@ "\n", "def normalize_text(text: str) -> str:\n", " text = unidecode.unidecode(text.lower())\n", - " text = re.sub(r'[_\\-,.;:]+', '', text)\n", + " text = re.sub(r\"[_\\-,.;:]+\", \"\", text)\n", " return text" ] }, @@ -1167,7 +1183,7 @@ "\n", " if len(output_dict[\"choices\"]) == 1:\n", " output_dict[\"choices\"] = output_dict[\"choices\"][0]\n", - " \n", + "\n", " return output_dict" ] }, @@ -1190,8 +1206,10 @@ "outputs": [], "source": [ "viol_gen = [\n", - " pred_ent for pred_ent in pred_ents if pred_ent[\"label\"] == \"VIOLENCIA_DE_GENERO\"\n", - "]\n" + " pred_ent\n", + " for pred_ent in pred_ents\n", + " if pred_ent[\"label\"] == \"VIOLENCIA_DE_GENERO\"\n", + "]" ] }, { @@ -1240,8 +1258,8 @@ " for violence_type, pattern in violence_type_patterns.items():\n", " if re.search(pattern, normalized_pred):\n", " found_types.add(violence_type)\n", - " \n", - " return list(found_types)\n" + "\n", + " return list(found_types)" ] }, { @@ -1455,7 +1473,7 @@ " if re.search(pattern, normalized_pred):\n", " found_modalities.add(modality)\n", "\n", - " return list(found_modalities)\n" + " return list(found_modalities)" ] }, { @@ -1582,7 +1600,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"RELACION_Y_TIPO_ENTRE_ACUSADO/A_Y_DENUNCIANTE\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -1596,7 +1614,9 @@ "source": [ "relationship_choices = []\n", "for sample in annots:\n", - " relationship = list(filter(filter_relationship_choices, sample[\"annotations\"][0][\"result\"]))\n", + " relationship = list(\n", + " filter(filter_relationship_choices, sample[\"annotations\"][0][\"result\"])\n", + " )\n", " relationship_choices.extend(relationship)" ] }, @@ -1718,7 +1738,7 @@ "source": [ "def find_relationship_type(pred: str) -> list:\n", " normalized_pred = normalize_text(pred)\n", - " \n", + "\n", " relationship_type_patterns = {\n", " \"familiar\": r\"(?:familiar?|hij[ao]s?|[mp]adres?|herman[ao]s?)\",\n", " \"pareja\": r\"(? list:\n", " normalized_pred = normalize_text(pred)\n", - " \n", + "\n", " resolution_type_patterns = {\n", " \"interlocutoria\": r\"interlocut[oa]ria\",\n", " \"definitiva\": r\"definitiv[ao]\",\n", " }\n", - " \n", + "\n", " found_types = set()\n", - " \n", + "\n", " for res_type, pattern in resolution_type_patterns.items():\n", " if re.search(pattern, normalized_pred):\n", " found_types.add(res_type)\n", - " \n", + "\n", " return list(found_types)" ] }, @@ -2058,7 +2080,9 @@ }, "outputs": [], "source": [ - "resolution_texts.loc[(resolution_texts[\"types\"].map(len) == 0)].sort_values(\"text\")" + "resolution_texts.loc[(resolution_texts[\"types\"].map(len) == 0)].sort_values(\n", + " \"text\"\n", + ")" ] }, { @@ -2149,7 +2173,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"NIVEL_INSTRUCCION_CAT\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -2163,7 +2187,9 @@ "source": [ "education_choices = []\n", "for sample in annots:\n", - " education = list(filter(filter_education_choices, sample[\"annotations\"][0][\"result\"]))\n", + " education = list(\n", + " filter(filter_education_choices, sample[\"annotations\"][0][\"result\"])\n", + " )\n", " education_choices.extend(education)" ] }, @@ -2273,7 +2299,7 @@ "source": [ "def find_education_level(pred: str) -> list:\n", " normalized_pred = normalize_text(pred)\n", - " \n", + "\n", " education_level_patterns = {\n", " # \"sin_instruccion\": r\"\",\n", " \"sin_escolarizar\": r\"(?:no (?:posee|termino)|sin).+estudios?\",\n", @@ -2290,13 +2316,13 @@ " \"universitario_completo\": r\"(?:universitari[ao]s? (?!in)complet[ao]s?|(? 0)].drop_duplicates(\"text\")" + "education_texts.loc[(education_texts[\"types\"].map(len) > 0)].drop_duplicates(\n", + " \"text\"\n", + ")" ] }, { @@ -2348,7 +2376,9 @@ }, "outputs": [], "source": [ - "education_texts.loc[(education_texts[\"types\"].map(len) == 0)].sort_values(\"text\")" + "education_texts.loc[(education_texts[\"types\"].map(len) == 0)].sort_values(\n", + " \"text\"\n", + ")" ] }, { @@ -2360,7 +2390,9 @@ }, "outputs": [], "source": [ - "education_texts.loc[(education_texts[\"types\"].map(len) > 1)].sort_values(\"text\")" + "education_texts.loc[(education_texts[\"types\"].map(len) > 1)].sort_values(\n", + " \"text\"\n", + ")" ] }, { @@ -2450,7 +2482,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"GENERO_CAT\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -2464,7 +2496,9 @@ "source": [ "gender_choices = []\n", "for sample in annots:\n", - " gender = list(filter(filter_gender_choices, sample[\"annotations\"][0][\"result\"]))\n", + " gender = list(\n", + " filter(filter_gender_choices, sample[\"annotations\"][0][\"result\"])\n", + " )\n", " gender_choices.extend(gender)" ] }, @@ -2574,7 +2608,7 @@ "source": [ "def find_gender(pred: str) -> list:\n", " normalized_pred = normalize_text(pred)\n", - " \n", + "\n", " gender_patterns = {\n", " \"varon_cis\": r\"varon(?:es)? cis\",\n", " \"mujer_cis\": r\"mujer(?:es)? cis\",\n", @@ -2583,13 +2617,13 @@ " \"travesti\": r\"travesti\",\n", " \"no_binaria\": r\"no ?binari[aoex]\",\n", " }\n", - " \n", + "\n", " found_gender = set()\n", - " \n", + "\n", " for gender, pattern in gender_patterns.items():\n", " if re.search(pattern, normalized_pred):\n", " found_gender.add(gender)\n", - " \n", + "\n", " return list(found_gender)" ] }, @@ -2695,7 +2729,9 @@ "metadata": {}, "outputs": [], "source": [ - "not_determined[\"beginning\"] = not_determined[\"label\"].str.startswith(\"B\").astype(int)\n", + "not_determined[\"beginning\"] = (\n", + " not_determined[\"label\"].str.startswith(\"B\").astype(int)\n", + ")\n", "not_determined.head(10)" ] }, @@ -2743,7 +2779,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"PERSONA_ACUSADA_NO_DETERMINADA\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -2757,7 +2793,11 @@ "source": [ "not_determined_choices = []\n", "for sample in annots:\n", - " not_determined = list(filter(filter_not_determined_choices, sample[\"annotations\"][0][\"result\"]))\n", + " not_determined = list(\n", + " filter(\n", + " filter_not_determined_choices, sample[\"annotations\"][0][\"result\"]\n", + " )\n", + " )\n", " not_determined_choices.extend(not_determined)" ] }, @@ -2869,7 +2909,7 @@ "source": [ "def find_gender(pred: str) -> list:\n", " normalized_pred = normalize_text(pred)\n", - " \n", + "\n", " gender_patterns = {\n", " \"varon_cis\": r\"varon(?:es)? cis\",\n", " \"mujer_cis\": r\"mujer(?:es)? cis\",\n", @@ -2878,13 +2918,13 @@ " \"travesti\": r\"travesti\",\n", " \"no_binaria\": r\"no ?binari[aoex]\",\n", " }\n", - " \n", + "\n", " found_gender = set()\n", - " \n", + "\n", " for gender, pattern in gender_patterns.items():\n", " if re.search(pattern, normalized_pred):\n", " found_gender.add(gender)\n", - " \n", + "\n", " return list(found_gender)" ] }, @@ -3038,7 +3078,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"CONDUCTA\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -3052,7 +3092,9 @@ "source": [ "conduct_choices = []\n", "for sample in annots:\n", - " conduct = list(filter(filter_conduct_choices, sample[\"annotations\"][0][\"result\"]))\n", + " conduct = list(\n", + " filter(filter_conduct_choices, sample[\"annotations\"][0][\"result\"])\n", + " )\n", " conduct_choices.extend(conduct)" ] }, @@ -3091,7 +3133,7 @@ " group_key=\"choices\",\n", " sort_key=\"choices\",\n", " )\n", - ")\n" + ")" ] }, { @@ -3389,7 +3431,7 @@ " \"violar_inhabilitacion_para_conducir\",\n", " \"violar_reglamentacion_juego\",\n", " \"zanjas_y_pozos_en_via_publica\",\n", - "]\n" + "]" ] }, { @@ -3425,7 +3467,7 @@ "# categories_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(categories))\n", "\n", "categories_embeddings = usem_qa.signatures[\"response_encoder\"](\n", - " input=tf.constant(categories), context=tf.constant(categories)\n", + " input=tf.constant(categories), context=tf.constant(categories)\n", ")\n", "\n", "# categories_embeddings = usem(categories)" @@ -3480,7 +3522,7 @@ "metadata": {}, "outputs": [], "source": [ - "#texts_embeddings = usem(texts)\n", + "# texts_embeddings = usem(texts)\n", "# texts_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(texts))" ] }, @@ -3543,7 +3585,7 @@ " top_3_similar = similarities.loc[text].sort_values(ascending=False)[:3]\n", " print(text)\n", " display(top_3_similar)\n", - " print(\"=\"*100)" + " print(\"=\" * 100)" ] }, { @@ -3553,13 +3595,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -3610,8 +3662,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -3653,8 +3709,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -3686,8 +3746,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -3729,13 +3793,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -3786,8 +3860,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -3829,8 +3907,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -3862,8 +3944,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -3905,13 +3991,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -3962,8 +4058,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -4005,8 +4105,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -4038,8 +4142,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -4081,13 +4189,21 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", " text_embeddings = usem([normalized_text])\n", - " similarities = np.inner(text_embeddings.numpy(), conduct_embeddings.numpy())\n", + " similarities = np.inner(\n", + " text_embeddings.numpy(), conduct_embeddings.numpy()\n", + " )\n", " top_k = np.ravel(np.argsort(-similarities))[:k]\n", - " \n", - " return np.array([categories])[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array([categories])[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -4128,8 +4244,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -4415,7 +4535,7 @@ " if re.search(pattern, normalized_pred):\n", " found_types.add(modality)\n", "\n", - " return list(found_types)\n" + " return list(found_types)" ] }, { @@ -4490,7 +4610,9 @@ "metadata": {}, "outputs": [], "source": [ - "conduct_desc[\"beginning\"] = conduct_desc[\"label\"].str.startswith(\"B\").astype(int)\n", + "conduct_desc[\"beginning\"] = (\n", + " conduct_desc[\"label\"].str.startswith(\"B\").astype(int)\n", + ")\n", "conduct_desc.head(10)" ] }, @@ -4538,7 +4660,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"CONDUCTA_DESCRIPCION\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -4552,7 +4674,9 @@ "source": [ "conduct_desc_choices = []\n", "for sample in annots:\n", - " conduct_desc = list(filter(filter_conduct_desc_choices, sample[\"annotations\"][0][\"result\"]))\n", + " conduct_desc = list(\n", + " filter(filter_conduct_desc_choices, sample[\"annotations\"][0][\"result\"])\n", + " )\n", " conduct_desc_choices.extend(conduct_desc)" ] }, @@ -4591,7 +4715,7 @@ " group_key=\"choices\",\n", " sort_key=\"choices\",\n", " )\n", - ")\n" + ")" ] }, { @@ -4789,7 +4913,7 @@ "# categories_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(categories))\n", "\n", "categories_embeddings = usem_qa.signatures[\"response_encoder\"](\n", - " input=tf.constant(categories) , context=tf.constant(categories)\n", + " input=tf.constant(categories), context=tf.constant(categories)\n", ")\n", "\n", "# categories_embeddings = usem(categories)" @@ -4802,7 +4926,9 @@ "metadata": {}, "outputs": [], "source": [ - "np.save(\"conduct_descr_embeddings.npy\", categories_embeddings[\"outputs\"].numpy())" + "np.save(\n", + " \"conduct_descr_embeddings.npy\", categories_embeddings[\"outputs\"].numpy()\n", + ")" ] }, { @@ -4854,8 +4980,10 @@ "metadata": {}, "outputs": [], "source": [ - "#texts_embeddings = usem(texts)\n", - "texts_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(texts))" + "# texts_embeddings = usem(texts)\n", + "texts_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(texts)\n", + ")" ] }, { @@ -4867,7 +4995,9 @@ }, "outputs": [], "source": [ - "similarities = np.inner(texts_embeddings[\"outputs\"], categories_embeddings[\"outputs\"])\n", + "similarities = np.inner(\n", + " texts_embeddings[\"outputs\"], categories_embeddings[\"outputs\"]\n", + ")\n", "similarities = pd.DataFrame(similarities, index=texts, columns=categories)\n", "similarities" ] @@ -4885,7 +5015,7 @@ " top_3_similar = similarities.loc[text].sort_values(ascending=False)[:3]\n", " print(text)\n", " display(top_3_similar)\n", - " print(\"=\"*100)" + " print(\"=\" * 100)" ] }, { @@ -4895,13 +5025,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -4952,8 +5092,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -4995,8 +5139,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -5028,8 +5176,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -5071,7 +5223,9 @@ "metadata": {}, "outputs": [], "source": [ - "categories_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(categories))" + "categories_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(categories)\n", + ")" ] }, { @@ -5081,13 +5235,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -5138,8 +5302,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -5181,8 +5349,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -5214,8 +5386,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -5257,13 +5433,21 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", " text_embeddings = usem([normalized_text])\n", - " similarities = np.inner(text_embeddings.numpy(), conduct_embeddings.numpy())\n", + " similarities = np.inner(\n", + " text_embeddings.numpy(), conduct_embeddings.numpy()\n", + " )\n", " top_k = np.ravel(np.argsort(-similarities))[:k]\n", - " \n", - " return np.array([categories])[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array([categories])[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -5304,8 +5488,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -5591,7 +5779,7 @@ " if re.search(pattern, normalized_pred):\n", " found_types.add(modality)\n", "\n", - " return list(found_types)\n" + " return list(found_types)" ] }, { @@ -5714,7 +5902,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"DETALLE\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -5728,7 +5916,9 @@ "source": [ "detail_choices = []\n", "for sample in annots:\n", - " detail = list(filter(filter_detail_choices, sample[\"annotations\"][0][\"result\"]))\n", + " detail = list(\n", + " filter(filter_detail_choices, sample[\"annotations\"][0][\"result\"])\n", + " )\n", " detail_choices.extend(detail)" ] }, @@ -6170,7 +6360,7 @@ "# categories_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(categories))\n", "\n", "categories_embeddings = usem_qa.signatures[\"response_encoder\"](\n", - " input=tf.constant(categories) , context=tf.constant(categories)\n", + " input=tf.constant(categories), context=tf.constant(categories)\n", ")\n", "\n", "# categories_embeddings = usem(categories)" @@ -6235,8 +6425,10 @@ "metadata": {}, "outputs": [], "source": [ - "#texts_embeddings = usem(texts)\n", - "texts_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(texts))" + "# texts_embeddings = usem(texts)\n", + "texts_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(texts)\n", + ")" ] }, { @@ -6248,7 +6440,9 @@ }, "outputs": [], "source": [ - "similarities = np.inner(texts_embeddings[\"outputs\"], categories_embeddings[\"outputs\"])\n", + "similarities = np.inner(\n", + " texts_embeddings[\"outputs\"], categories_embeddings[\"outputs\"]\n", + ")\n", "similarities = pd.DataFrame(similarities, index=texts, columns=categories)\n", "similarities" ] @@ -6266,7 +6460,7 @@ " top_3_similar = similarities.loc[text].sort_values(ascending=False)[:3]\n", " print(text)\n", " display(top_3_similar)\n", - " print(\"=\"*100)" + " print(\"=\" * 100)" ] }, { @@ -6276,13 +6470,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -6333,8 +6537,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -6376,8 +6584,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -6409,8 +6621,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -6452,7 +6668,9 @@ "metadata": {}, "outputs": [], "source": [ - "categories_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(categories))" + "categories_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(categories)\n", + ")" ] }, { @@ -6462,13 +6680,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -6519,8 +6747,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -6562,8 +6794,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -6595,8 +6831,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -6638,13 +6878,21 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", " text_embeddings = usem([normalized_text])\n", - " similarities = np.inner(text_embeddings.numpy(), conduct_embeddings.numpy())\n", + " similarities = np.inner(\n", + " text_embeddings.numpy(), conduct_embeddings.numpy()\n", + " )\n", " top_k = np.ravel(np.argsort(-similarities))[:k]\n", - " \n", - " return np.array([categories])[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array([categories])[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -6685,8 +6933,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -6808,7 +7060,7 @@ " from_name = annot[\"from_name\"]\n", " if from_name == \"OBJETO_DE_LA_RESOLUCION\":\n", " return True\n", - " \n", + "\n", " except:\n", " return False" ] @@ -6822,7 +7074,9 @@ "source": [ "res_object_choices = []\n", "for sample in annots:\n", - " res_object = list(filter(filter_res_object_choices, sample[\"annotations\"][0][\"result\"]))\n", + " res_object = list(\n", + " filter(filter_res_object_choices, sample[\"annotations\"][0][\"result\"])\n", + " )\n", " res_object_choices.extend(res_object)" ] }, @@ -7012,7 +7266,7 @@ "# categories_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(categories))\n", "\n", "categories_embeddings = usem_qa.signatures[\"response_encoder\"](\n", - " input=tf.constant(categories) , context=tf.constant(categories)\n", + " input=tf.constant(categories), context=tf.constant(categories)\n", ")\n", "\n", "# categories_embeddings = usem(categories)" @@ -7077,8 +7331,10 @@ "metadata": {}, "outputs": [], "source": [ - "#texts_embeddings = usem(texts)\n", - "texts_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(texts))" + "# texts_embeddings = usem(texts)\n", + "texts_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(texts)\n", + ")" ] }, { @@ -7090,7 +7346,9 @@ }, "outputs": [], "source": [ - "similarities = np.inner(texts_embeddings[\"outputs\"], categories_embeddings[\"outputs\"])\n", + "similarities = np.inner(\n", + " texts_embeddings[\"outputs\"], categories_embeddings[\"outputs\"]\n", + ")\n", "similarities = pd.DataFrame(similarities, index=texts, columns=categories)\n", "similarities" ] @@ -7108,7 +7366,7 @@ " top_3_similar = similarities.loc[text].sort_values(ascending=False)[:3]\n", " print(text)\n", " display(top_3_similar)\n", - " print(\"=\"*100)" + " print(\"=\" * 100)" ] }, { @@ -7118,13 +7376,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -7165,8 +7433,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -7208,8 +7480,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -7241,8 +7517,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -7284,7 +7564,9 @@ "metadata": {}, "outputs": [], "source": [ - "categories_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(categories))" + "categories_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(categories)\n", + ")" ] }, { @@ -7294,13 +7576,23 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", - " text_embeddings = usem_qa.signatures[\"question_encoder\"](input=tf.constant(normalized_text))\n", - " similarities = np.inner(text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"])\n", + " text_embeddings = usem_qa.signatures[\"question_encoder\"](\n", + " input=tf.constant(normalized_text)\n", + " )\n", + " similarities = np.inner(\n", + " text_embeddings[\"outputs\"], conduct_embeddings[\"outputs\"]\n", + " )\n", " top_k = np.argsort(np.ravel(-similarities))[:k]\n", - " \n", - " return np.array(categories)[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array(categories)[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -7351,8 +7643,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=2))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=2)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -7394,8 +7690,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -7427,8 +7727,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=5))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=5)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -7470,13 +7774,21 @@ "metadata": {}, "outputs": [], "source": [ - "def find_top_k_similar(text, conduct_embeddings=categories_embeddings, categories=categories, k=1):\n", + "def find_top_k_similar(\n", + " text, conduct_embeddings=categories_embeddings, categories=categories, k=1\n", + "):\n", " normalized_text = normalize_text(text)\n", " text_embeddings = usem([normalized_text])\n", - " similarities = np.inner(text_embeddings.numpy(), conduct_embeddings.numpy())\n", + " similarities = np.inner(\n", + " text_embeddings.numpy(), conduct_embeddings.numpy()\n", + " )\n", " top_k = np.ravel(np.argsort(-similarities))[:k]\n", - " \n", - " return np.array([categories])[top_k][0] if k == 1 else np.array(categories)[top_k]" + "\n", + " return (\n", + " np.array([categories])[top_k][0]\n", + " if k == 1\n", + " else np.array(categories)[top_k]\n", + " )" ] }, { @@ -7517,8 +7829,12 @@ "metadata": {}, "outputs": [], "source": [ - "grouped[\"pred_choices\"] = grouped[\"text\"].map(lambda x: find_top_k_similar(x, k=3))\n", - "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(lambda x: [string.replace(\" \", \"_\") for string in x])" + "grouped[\"pred_choices\"] = grouped[\"text\"].map(\n", + " lambda x: find_top_k_similar(x, k=3)\n", + ")\n", + "grouped[\"pred_choices\"] = grouped[\"pred_choices\"].map(\n", + " lambda x: [string.replace(\" \", \"_\") for string in x]\n", + ")" ] }, { @@ -7580,7 +7896,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "aymurai (3.10.19)", "language": "python", "name": "python3" }, @@ -7594,12 +7910,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.8 (main, Oct 12 2022, 19:14:26) [GCC 9.4.0]" - }, - "vscode": { - "interpreter": { - "hash": "e7370f93d1d0cde622a1f8e1c04877d8463912d04d973331ad4851f04de6915a" - } + "version": "3.10.19" } }, "nbformat": 4, diff --git a/pyproject.toml b/pyproject.toml index 37d72eb0..94e817e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,6 @@ dependencies = [ "numpy<2.0.0", "more-itertools>=10.5.0", "odfpy>=1.4.1", - "gdown==4.6.0", "joblib>=1.4.2", "datetime_matcher @ git+https://github.com/jedzill4/datetime_matcher", "jiwer==3.0.5", @@ -75,13 +74,11 @@ dependencies = [ "scipy<1.14.1", "torch>=1.13.1", "pytorch-lightning==1.8.3.post1", - "tensorflow_text==2.10.0; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'x86_64') or sys_platform == 'win32'", # no ARM64 macOS wheels "psutil==6.1.0", "sqlmodel==0.0.22", "alembic>=1.13.3", "tenacity>=9.0.0", "python-dotenv>=1.0.1", - "tensorflow-hub>=0.16.1", "sentence-transformers>=2.2.0", "pymupdf>=1.25.2", "pymupdf4llm>=0.0.17", diff --git a/resources/pipelines/production/full-paragraph/pipeline.json b/resources/pipelines/production/full-paragraph/pipeline.json index b3636d2a..b5da2f9e 100644 --- a/resources/pipelines/production/full-paragraph/pipeline.json +++ b/resources/pipelines/production/full-paragraph/pipeline.json @@ -26,39 +26,35 @@ {} ], [ - "aymurai.transforms.entity_subcategories.usem.USEMSubcategorizer", + "aymurai.transforms.entity_subcategories.sentence_transformer.SentenceTransformerSubcategorizer", { "category": "CONDUCTA", - "subcategories_path": "https://drive.google.com/uc?id=1Vj5BxyeHzDnR1T8jYjLuteM3YwzE7fTW&confirm=true", - "response_embeddings_path": "https://drive.google.com/uc?id=1zvBHGf1MeFyyG_I0TukJl1eaM-7TsbPF&confirm=true", - "device": "/cpu:0" + "embeddings_path": "conducta.npz", + "device": "cpu" } ], [ - "aymurai.transforms.entity_subcategories.usem.USEMSubcategorizer", + "aymurai.transforms.entity_subcategories.sentence_transformer.SentenceTransformerSubcategorizer", { "category": "CONDUCTA_DESCRIPCION", - "subcategories_path": "https://drive.google.com/uc?id=1A1I9xwzvynwxSv1I0SDHhN216Z3Yvoqj&confirm=true", - "response_embeddings_path": "https://drive.google.com/uc?id=1c3nYVDIq23kYqgMIIKGtDbIz6zDORpYK&confirm=true", - "device": "/cpu:0" + "embeddings_path": "conducta_descripcion.npz", + "device": "cpu" } ], [ - "aymurai.transforms.entity_subcategories.usem.USEMSubcategorizer", + "aymurai.transforms.entity_subcategories.sentence_transformer.SentenceTransformerSubcategorizer", { "category": "DETALLE", - "subcategories_path": "https://drive.google.com/uc?id=1o1Z4fhGTtNzUIL2m3WOfDr_f0KXHu_Ms&confirm=true", - "response_embeddings_path": "https://drive.google.com/uc?id=1OumPgnnM9ffjHjObnb5NL96e3hnlt7Ik&confirm=true", - "device": "/cpu:0" + "embeddings_path": "detalle.npz", + "device": "cpu" } ], [ - "aymurai.transforms.entity_subcategories.usem.USEMSubcategorizer", + "aymurai.transforms.entity_subcategories.sentence_transformer.SentenceTransformerSubcategorizer", { "category": "OBJETO_DE_LA_RESOLUCION", - "subcategories_path": "https://drive.google.com/uc?id=1ksmfX_AJaE-OFEEGzj2N2mZgg5HZWB_4&confirm=true", - "response_embeddings_path": "https://drive.google.com/uc?id=18wOgqzNDsqF13nrvX2XscE0JS_xrgqBU&confirm=true", - "device": "/cpu:0" + "embeddings_path": "objeto_de_la_resolucion.npz", + "device": "cpu" } ], [ diff --git a/uv.lock b/uv.lock index 3acc56ea..e8a103b2 100644 --- a/uv.lock +++ b/uv.lock @@ -241,19 +241,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, ] -[[package]] -name = "astunparse" -version = "1.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, - { name = "wheel" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/af/4182184d3c338792894f34a62672919db7ca008c89abee9b564dd34d8029/astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872", size = 18290 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/03/13dde6512ad7b4557eb792fbcf0c653af6076b81e5941d36ec61f7ce6028/astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8", size = 12732 }, -] - [[package]] name = "async-lru" version = "2.0.5" @@ -298,7 +285,7 @@ wheels = [ [[package]] name = "aymurai" -version = "2.0.0a2.dev9" +version = "2.0.0a3.dev9" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -310,7 +297,6 @@ dependencies = [ { name = "faker" }, { name = "fastapi", extra = ["standard"] }, { name = "flair" }, - { name = "gdown" }, { name = "jiwer" }, { name = "joblib" }, { name = "langextract", extra = ["openai"] }, @@ -335,8 +321,6 @@ dependencies = [ { name = "sentencepiece" }, { name = "sqlmodel" }, { name = "tenacity" }, - { name = "tensorflow-hub" }, - { name = "tensorflow-text", marker = "(platform_machine == 'x86_64' and sys_platform == 'darwin') or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "tiktoken" }, { name = "torch" }, { name = "unidecode" }, @@ -370,7 +354,6 @@ requires-dist = [ { name = "faker", specifier = "==18.11.2" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.6" }, { name = "flair", specifier = "==0.15.1" }, - { name = "gdown", specifier = "==4.6.0" }, { name = "jiwer", specifier = "==3.0.5" }, { name = "joblib", specifier = ">=1.4.2" }, { name = "langextract", extras = ["openai"], specifier = "==1.1.0" }, @@ -395,8 +378,6 @@ requires-dist = [ { name = "sentencepiece", specifier = "==0.2.0" }, { name = "sqlmodel", specifier = "==0.0.22" }, { name = "tenacity", specifier = ">=9.0.0" }, - { name = "tensorflow-hub", specifier = ">=0.16.1" }, - { name = "tensorflow-text", marker = "(platform_machine == 'x86_64' and sys_platform == 'darwin') or sys_platform == 'linux' or sys_platform == 'win32'", specifier = "==2.10.0" }, { name = "tiktoken", specifier = "==0.12.0" }, { name = "torch", specifier = ">=1.13.1" }, { name = "unidecode", specifier = "==1.3.8" }, @@ -1112,15 +1093,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, ] -[[package]] -name = "flatbuffers" -version = "25.9.23" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/1f/3ee70b0a55137442038f2a33469cc5fddd7e0ad2abf83d7497c18a2b6923/flatbuffers-25.9.23.tar.gz", hash = "sha256:676f9fa62750bb50cf531b42a0a2a118ad8f7f797a511eda12881c016f093b12", size = 22067 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/1b/00a78aa2e8fbd63f9af08c9c19e6deb3d5d66b4dda677a0f61654680ee89/flatbuffers-25.9.23-py2.py3-none-any.whl", hash = "sha256:255538574d6cb6d0a79a17ec8bc0d30985913b87513a01cce8bcdb6b4c44d0e2", size = 30869 }, -] - [[package]] name = "fonttools" version = "4.61.0" @@ -1198,15 +1170,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, ] -[[package]] -name = "gast" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/4a/07c7e59cef23fb147454663c3271c21da68ba2ab141427c20548ae5a8a4d/gast-0.4.0.tar.gz", hash = "sha256:40feb7b8b8434785585ab224d1568b857edb18297e5a3047f1ba012bc83b42c1", size = 13804 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/48/583c032b79ae5b3daa02225a675aeb673e58d2cb698e78510feceb11958c/gast-0.4.0-py3-none-any.whl", hash = "sha256:b7adcdd5adbebf1adf17378da5ba3f543684dbec47b1cda1f3997e573cd542c4", size = 9824 }, -] - [[package]] name = "gdown" version = "4.6.0" @@ -1291,19 +1254,6 @@ requests = [ { name = "requests" }, ] -[[package]] -name = "google-auth-oauthlib" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "requests-oauthlib" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/21/b84fa7ef834d4b126faad13da6e582c8f888e196326b9d6aab1ae303df4f/google-auth-oauthlib-0.4.6.tar.gz", hash = "sha256:a90a072f6993f2c327067bf65270046384cda5a8ecb20b94ea9a687f1f233a7a", size = 19516 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/0e/0636cc1448a7abc444fb1b3a63655e294e0d2d49092dc3de05241be6d43c/google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73", size = 18306 }, -] - [[package]] name = "google-cloud-core" version = "2.5.0" @@ -1369,18 +1319,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/f2/97fefdd1ad1f3428321bac819ae7a83ccc59f6439616054736b7819fa56c/google_genai-1.53.0-py3-none-any.whl", hash = "sha256:65a3f99e5c03c372d872cda7419f5940e723374bb12a2f3ffd5e3e56e8eb2094", size = 262015 }, ] -[[package]] -name = "google-pasta" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/35/4a/0bd53b36ff0323d10d5f24ebd67af2de10a1117f5cf4d7add90df92756f1/google-pasta-0.2.0.tar.gz", hash = "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e", size = 40430 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/de/c648ef6835192e6e2cc03f40b19eeda4382c49b5bafb43d88b931c4c74ac/google_pasta-0.2.0-py3-none-any.whl", hash = "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed", size = 57471 }, -] - [[package]] name = "google-resumable-media" version = "2.8.0" @@ -1421,23 +1359,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740 }, ] -[[package]] -name = "grpcio" -version = "1.67.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/53/d9282a66a5db45981499190b77790570617a604a38f3d103d0400974aeb5/grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732", size = 12580022 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/cd/f6ca5c49aa0ae7bc6d0757f7dae6f789569e9490a635eaabe02bc02de7dc/grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f", size = 5112450 }, - { url = "https://files.pythonhosted.org/packages/d4/f0/d9bbb4a83cbee22f738ee7a74aa41e09ccfb2dcea2cc30ebe8dab5b21771/grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d", size = 10937518 }, - { url = "https://files.pythonhosted.org/packages/5b/17/0c5dbae3af548eb76669887642b5f24b232b021afe77eb42e22bc8951d9c/grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f", size = 5633610 }, - { url = "https://files.pythonhosted.org/packages/17/48/e000614e00153d7b2760dcd9526b95d72f5cfe473b988e78f0ff3b472f6c/grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0", size = 6240678 }, - { url = "https://files.pythonhosted.org/packages/64/19/a16762a70eeb8ddfe43283ce434d1499c1c409ceec0c646f783883084478/grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa", size = 5884528 }, - { url = "https://files.pythonhosted.org/packages/6b/dc/bd016aa3684914acd2c0c7fa4953b2a11583c2b844f3d7bae91fa9b98fbb/grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292", size = 6583680 }, - { url = "https://files.pythonhosted.org/packages/1a/93/1441cb14c874f11aa798a816d582f9da82194b6677f0f134ea53d2d5dbeb/grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311", size = 6162967 }, - { url = "https://files.pythonhosted.org/packages/29/e9/9295090380fb4339b7e935b9d005fa9936dd573a22d147c9e5bb2df1b8d4/grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed", size = 3616336 }, - { url = "https://files.pythonhosted.org/packages/ce/de/7c783b8cb8f02c667ca075c49680c4aeb8b054bc69784bcb3e7c1bbf4985/grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e", size = 4352071 }, -] - [[package]] name = "h11" version = "0.16.0" @@ -1447,24 +1368,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] -[[package]] -name = "h5py" -version = "3.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/6a/0d79de0b025aa85dc8864de8e97659c94cf3d23148394a954dc5ca52f8c8/h5py-3.15.1.tar.gz", hash = "sha256:c86e3ed45c4473564de55aa83b6fc9e5ead86578773dfbd93047380042e26b69", size = 426236 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/30/8fa61698b438dd751fa46a359792e801191dadab560d0a5f1c709443ef8e/h5py-3.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67e59f6c2f19a32973a40f43d9a088ae324fe228c8366e25ebc57ceebf093a6b", size = 3414477 }, - { url = "https://files.pythonhosted.org/packages/16/16/db2f63302937337c4e9e51d97a5984b769bdb7488e3d37632a6ac297f8ef/h5py-3.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e2f471688402c3404fa4e13466e373e622fd4b74b47b56cfdff7cc688209422", size = 2850298 }, - { url = "https://files.pythonhosted.org/packages/fc/2e/f1bb7de9b05112bfd14d5206090f0f92f1e75bbb412fbec5d4653c3d44dd/h5py-3.15.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c45802bcb711e128a6839cb6c01e9ac648dc55df045c9542a675c771f15c8d5", size = 4523605 }, - { url = "https://files.pythonhosted.org/packages/05/8a/63f4b08f3628171ce8da1a04681a65ee7ac338fde3cb3e9e3c9f7818e4da/h5py-3.15.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64ce3f6470adb87c06e3a8dd1b90e973699f1759ad79bfa70c230939bff356c9", size = 4735346 }, - { url = "https://files.pythonhosted.org/packages/74/48/f16d12d9de22277605bcc11c0dcab5e35f06a54be4798faa2636b5d44b3c/h5py-3.15.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4411c1867b9899a25e983fff56d820a66f52ac326bbe10c7cdf7d832c9dcd883", size = 4175305 }, - { url = "https://files.pythonhosted.org/packages/d6/2f/47cdbff65b2ce53c27458c6df63a232d7bb1644b97df37b2342442342c84/h5py-3.15.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2cbc4104d3d4aca9d6db8c0c694555e255805bfeacf9eb1349bda871e26cacbe", size = 4653602 }, - { url = "https://files.pythonhosted.org/packages/c3/28/dc08de359c2f43a67baa529cb70d7f9599848750031975eed92d6ae78e1d/h5py-3.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:01f55111ca516f5568ae7a7fc8247dfce607de331b4467ee8a9a6ed14e5422c7", size = 2873601 }, -] - [[package]] name = "hf-xet" version = "1.2.0" @@ -2017,27 +1920,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, ] -[[package]] -name = "keras" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/4d/dc255a437c9616b155e5bd55e325e092b7cdcb4652361d733ae051d40853/keras-2.10.0-py2.py3-none-any.whl", hash = "sha256:26a6e2c2522e7468ddea22710a99b3290493768fc08a39e75d1173a0e3452fdf", size = 1684294 }, -] - -[[package]] -name = "keras-preprocessing" -version = "1.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f1/b44337faca48874333769a29398fe4666686733c8880aa160b9fd5dfe600/Keras_Preprocessing-1.1.2.tar.gz", hash = "sha256:add82567c50c8bc648c14195bf544a5ce7c1f76761536956c3d2978970179ef3", size = 163598 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/4c/7c3275a01e12ef9368a892926ab932b33bb13d55794881e3573482b378a7/Keras_Preprocessing-1.1.2-py2.py3-none-any.whl", hash = "sha256:7b82029b130ff61cc99b55f3bd27427df4838576838c5b2f65940e4fcec99a7b", size = 42581 }, -] - [[package]] name = "kiwisolver" version = "1.4.9" @@ -2114,23 +1996,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, ] -[[package]] -name = "libclang" -version = "18.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6e/5c/ca35e19a4f142adffa27e3d652196b7362fa612243e2b916845d801454fc/libclang-18.1.1.tar.gz", hash = "sha256:a1214966d08d73d971287fc3ead8dfaf82eb07fb197680d8b3859dbbbbf78250", size = 39612 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/49/f5e3e7e1419872b69f6f5e82ba56e33955a74bd537d8a1f5f1eff2f3668a/libclang-18.1.1-1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b2e143f0fac830156feb56f9231ff8338c20aecfe72b4ffe96f19e5a1dbb69a", size = 25836045 }, - { url = "https://files.pythonhosted.org/packages/e2/e5/fc61bbded91a8830ccce94c5294ecd6e88e496cc85f6704bf350c0634b70/libclang-18.1.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6f14c3f194704e5d09769108f03185fce7acaf1d1ae4bbb2f30a72c2400cb7c5", size = 26502641 }, - { url = "https://files.pythonhosted.org/packages/db/ed/1df62b44db2583375f6a8a5e2ca5432bbdc3edb477942b9b7c848c720055/libclang-18.1.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:83ce5045d101b669ac38e6da8e58765f12da2d3aafb3b9b98d88b286a60964d8", size = 26420207 }, - { url = "https://files.pythonhosted.org/packages/1d/fc/716c1e62e512ef1c160e7984a73a5fc7df45166f2ff3f254e71c58076f7c/libclang-18.1.1-py2.py3-none-manylinux2010_x86_64.whl", hash = "sha256:c533091d8a3bbf7460a00cb6c1a71da93bffe148f172c7d03b1c31fbf8aa2a0b", size = 24515943 }, - { url = "https://files.pythonhosted.org/packages/3c/3d/f0ac1150280d8d20d059608cf2d5ff61b7c3b7f7bcf9c0f425ab92df769a/libclang-18.1.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:54dda940a4a0491a9d1532bf071ea3ef26e6dbaf03b5000ed94dd7174e8f9592", size = 23784972 }, - { url = "https://files.pythonhosted.org/packages/fe/2f/d920822c2b1ce9326a4c78c0c2b4aa3fde610c7ee9f631b600acb5376c26/libclang-18.1.1-py2.py3-none-manylinux2014_armv7l.whl", hash = "sha256:cf4a99b05376513717ab5d82a0db832c56ccea4fd61a69dbb7bccf2dfb207dbe", size = 20259606 }, - { url = "https://files.pythonhosted.org/packages/2d/c2/de1db8c6d413597076a4259cea409b83459b2db997c003578affdd32bf66/libclang-18.1.1-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:69f8eb8f65c279e765ffd28aaa7e9e364c776c17618af8bff22a8df58677ff4f", size = 24921494 }, - { url = "https://files.pythonhosted.org/packages/0b/2d/3f480b1e1d31eb3d6de5e3ef641954e5c67430d5ac93b7fa7e07589576c7/libclang-18.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:4dd2d3b82fab35e2bf9ca717d7b63ac990a3519c7e312f19fa8e86dcc712f7fb", size = 26415083 }, - { url = "https://files.pythonhosted.org/packages/71/cf/e01dc4cc79779cd82d77888a88ae2fa424d93b445ad4f6c02bfc18335b70/libclang-18.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:3f0e1f49f04d3cd198985fea0511576b0aee16f9ff0e0f0cad7f9c57ec3c20e8", size = 22361112 }, -] - [[package]] name = "lightning-utilities" version = "0.3.0" @@ -2216,15 +2081,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, ] -[[package]] -name = "markdown" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678 }, -] - [[package]] name = "markdown-it-py" version = "4.0.0" @@ -2758,15 +2614,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, ] -[[package]] -name = "oauthlib" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 }, -] - [[package]] name = "odfpy" version = "1.4.1" @@ -2825,15 +2672,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, ] -[[package]] -name = "opt-einsum" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932 }, -] - [[package]] name = "optuna" version = "4.6.0" @@ -3639,19 +3477,6 @@ socks = [ { name = "pysocks" }, ] -[[package]] -name = "requests-oauthlib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 }, -] - [[package]] name = "rfc3339-validator" version = "0.1.4" @@ -4157,47 +3982,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248 }, ] -[[package]] -name = "tensorboard" -version = "2.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "google-auth" }, - { name = "google-auth-oauthlib" }, - { name = "grpcio" }, - { name = "markdown" }, - { name = "numpy" }, - { name = "protobuf" }, - { name = "requests" }, - { name = "setuptools" }, - { name = "tensorboard-data-server" }, - { name = "tensorboard-plugin-wit" }, - { name = "werkzeug" }, - { name = "wheel" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/49/a5ec29886ef823718c8ae54ed0b3ad7e19066b5bf21cec5038427e6a04c4/tensorboard-2.10.1-py3-none-any.whl", hash = "sha256:fb9222c1750e2fa35ef170d998a1e229f626eeced3004494a8849c88c15d8c1c", size = 5873392 }, -] - -[[package]] -name = "tensorboard-data-server" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/69/5747a957f95e2e1d252ca41476ae40ce79d70d38151d2e494feb7722860c/tensorboard_data_server-0.6.1-py3-none-any.whl", hash = "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7", size = 2350 }, - { url = "https://files.pythonhosted.org/packages/3e/48/dd135dbb3cf16bfb923720163493cab70e7336db4b5f3103d49efa730404/tensorboard_data_server-0.6.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:fa8cef9be4fcae2f2363c88176638baf2da19c5ec90addb49b1cde05c95c88ee", size = 3546350 }, - { url = "https://files.pythonhosted.org/packages/60/f9/802efd84988bffd9f644c03b6e66fde8e76c3aa33db4279ddd11c5d61f4b/tensorboard_data_server-0.6.1-py3-none-manylinux2010_x86_64.whl", hash = "sha256:d8237580755e58eff68d1f3abefb5b1e39ae5c8b127cc40920f9c4fb33f4b98a", size = 4910134 }, -] - -[[package]] -name = "tensorboard-plugin-wit" -version = "1.8.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/68/e8ecfac5dd594b676c23a7f07ea34c197d7d69b3313afdf8ac1b0a9905a2/tensorboard_plugin_wit-1.8.1-py3-none-any.whl", hash = "sha256:ff26bdd583d155aa951ee3b152b3d0cffae8005dc697f72b44a8e8c2a77a8cbe", size = 781327 }, -] - [[package]] name = "tensorboardx" version = "2.6" @@ -4212,87 +3996,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/9f/d532d37f10ac7af136d4c2ba71e1fe7af0f3cc0cc076dfc05826171e9737/tensorboardX-2.6-py2.py3-none-any.whl", hash = "sha256:24a7cd076488de1e9d15ef25371b8ebf90c4f8f622af2477c611198f03f4a606", size = 114480 }, ] -[[package]] -name = "tensorflow" -version = "2.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "astunparse" }, - { name = "flatbuffers" }, - { name = "gast" }, - { name = "google-pasta" }, - { name = "grpcio" }, - { name = "h5py" }, - { name = "keras" }, - { name = "keras-preprocessing" }, - { name = "libclang" }, - { name = "numpy" }, - { name = "opt-einsum" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "setuptools" }, - { name = "six" }, - { name = "tensorboard" }, - { name = "tensorflow-estimator" }, - { name = "tensorflow-io-gcs-filesystem" }, - { name = "termcolor" }, - { name = "typing-extensions" }, - { name = "wrapt" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/84/92f4f4c017ef2071412745034a98108106347478c56475c65d275bd2a792/tensorflow-2.10.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:dc3587dfa714be711d2681d5e2fb59037b18e83e692f084db49bce31b6268d15", size = 241225350 }, - { url = "https://files.pythonhosted.org/packages/27/35/50f68ad5c082836045b2b068d095b0ed5bb6fdee4dfcb9af76058df4ed66/tensorflow-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3cab933757eb0c204dc4cf34d031939e33cae8f97a7aaef00a12678129b17f", size = 1937 }, - { url = "https://files.pythonhosted.org/packages/b2/c3/668c91cc7074eed672691f130562c0f02d89aebf01f6e14f1741f7fb900b/tensorflow-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20f1d579b849afaea7b10f7693dc43b1d07321d279a016f01e2ddfe971d0d8af", size = 578143480 }, - { url = "https://files.pythonhosted.org/packages/ad/87/f484e0b86687c97d2dfb081e03e948b796561fc8608b409a9366e3b4a663/tensorflow-2.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6049664f9a0d14b0a4a7e6f058be87b2d8c27be826d7dd9a870ff03683fbc0b", size = 455948187 }, -] - -[[package]] -name = "tensorflow-estimator" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/05/9d7f3a6c783669cba36a6eb4555d0c73a516eee935dde6176dfb8512f94e/tensorflow_estimator-2.10.0-py2.py3-none-any.whl", hash = "sha256:f324ea17cd57f16e33bf188711d5077e6b2e5f5a12c328d6e01a07b23888edcd", size = 438698 }, -] - -[[package]] -name = "tensorflow-hub" -version = "0.16.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "protobuf" }, - { name = "tf-keras" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/50/00dba77925bf2a0a1e45d7bcf8a69a1d2534fb4bb277d9010bd148d2235e/tensorflow_hub-0.16.1-py2.py3-none-any.whl", hash = "sha256:e10c184b3d08daeafada11ffea2dd46781725b6bef01fad1f74d6634ad05311f", size = 30771 }, -] - -[[package]] -name = "tensorflow-io-gcs-filesystem" -version = "0.37.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/a3/12d7e7326a707919b321e2d6e4c88eb61596457940fd2b8ff3e9b7fac8a7/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:249c12b830165841411ba71e08215d0e94277a49c551e6dd5d72aab54fe5491b", size = 2470224 }, - { url = "https://files.pythonhosted.org/packages/1c/55/3849a188cc15e58fefde20e9524d124a629a67a06b4dc0f6c881cb3c6e39/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:257aab23470a0796978efc9c2bcf8b0bc80f22e6298612a4c0a50d3f4e88060c", size = 3479613 }, - { url = "https://files.pythonhosted.org/packages/e2/19/9095c69e22c879cb3896321e676c69273a549a3148c4f62aa4bc5ebdb20f/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8febbfcc67c61e542a5ac1a98c7c20a91a5e1afc2e14b1ef0cb7c28bc3b6aa70", size = 4842078 }, - { url = "https://files.pythonhosted.org/packages/f3/48/47b7d25572961a48b1de3729b7a11e835b888e41e0203cca82df95d23b91/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9679b36e3a80921876f31685ab6f7270f3411a4cc51bc2847e80d0e4b5291e27", size = 5085736 }, -] - -[[package]] -name = "tensorflow-text" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tensorflow", marker = "platform_machine != 'arm64' or platform_system != 'Darwin'" }, - { name = "tensorflow-hub" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/64/489c459f46f678ec4822b815ba9dfb1182c351e2208e98b6e83543254b55/tensorflow_text-2.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84b76847189d0d7bc8d4a130f0617e13bc9b810cf1feeb9c49da2906a2f39785", size = 5621257 }, - { url = "https://files.pythonhosted.org/packages/37/a5/bf11fb427226283b2abefcad355030effdb1a0c45978b80c63a38556bdd2/tensorflow_text-2.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:554f05956922afe0269dfb1e2fd73f5e049fa5746c47fd34d92ff7e447d746d4", size = 5889598 }, - { url = "https://files.pythonhosted.org/packages/88/a2/8d2ee50c8e5e355bf23975a0b7fa49ddd9c9b0ecb48b8c77d78ff4b3bcc0/tensorflow_text-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:269a3d7c3bd34e069e5d4ccaece902fc46aad65220301ee3bdb70d4ab031a60d", size = 5004273 }, -] - [[package]] name = "termcolor" version = "3.2.0" @@ -4316,15 +4019,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, ] -[[package]] -name = "tf-keras" -version = "2.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/0c/36054828137226dc3a559b525640f296a99ee8eb38beca32b36d29bb303b/tf_keras-2.15.0.tar.gz", hash = "sha256:d7559c2ba40667679fcb2105d3e4b68bbc07ecafbf1037462ce7b3974c3c6798", size = 1250420 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/26/ca8a6cca61f2a44f1e7ee71ebdb9c8dfbc4371f418db811cdca4641f6daa/tf_keras-2.15.0-py3-none-any.whl", hash = "sha256:48607ee60a4d1fa7c09d6a44293a803faf3136e7a43f92df089ac094117547d2", size = 1714973 }, -] - [[package]] name = "threadpoolctl" version = "3.6.0" @@ -4813,27 +4507,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, ] -[[package]] -name = "werkzeug" -version = "3.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/ea/b0f8eeb287f8df9066e56e831c7824ac6bab645dd6c7a8f4b2d767944f9b/werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e", size = 864687 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/f9/9e082990c2585c744734f85bec79b5dae5df9c974ffee58fe421652c8e91/werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905", size = 224960 }, -] - -[[package]] -name = "wheel" -version = "0.45.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494 }, -] - [[package]] name = "widgetsnbextension" version = "4.0.15" From 0a35efe9c22e668989ef1fb00a274be2dcac1f8f Mon Sep 17 00:00:00 2001 From: jed Date: Mon, 5 Jan 2026 17:47:24 -0300 Subject: [PATCH 027/101] =?UTF-8?q?WIP:=20feat(decision):=20=E2=9C=A8=20in?= =?UTF-8?q?tegrate=20TinyEmbeddingBagClassifier=20for=20decision=20detecti?= =?UTF-8?q?on=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(decision): ✨ integrate TinyEmbeddingBagClassifier for decision detection - Introduced a new model class `DecisionEmbeddingBagBinRegex` using `TinyEmbeddingBagClassifier`. - Updated model loading and saving mechanisms to support safetensors format. - Added a new training notebook for the embedding bag classifier. - Modified the pipeline configuration to include the new model. * ⚡️ Remove unidecode usage to avoid double normalization in model_input_from_text * 📝 Add type hints and docstrings for clarity in DecisionEmbeddingBagBinRegex and TinyEmbeddingBagClassifier * 🔧 Refactor import statements for safetensors to remove try-except block * 🔥 Remove Conv1dTextClassifier, Tokenizer and SpanishTokenizer implementations * 🐛 Fix gen_aymurai_entity call by removing unused category parameter * 🔖 Update aymurai package version to 2.0.0a4.dev1 --- aymurai/models/decision/binregex.py | 197 ++++-- aymurai/models/decision/conv1d.py | 138 ----- aymurai/models/decision/embeddingbag.py | 267 +++++++++ aymurai/models/decision/tokenizer.py | 36 -- aymurai/text/tokenizers/__init__.py | 0 aymurai/text/tokenizers/spanish.py | 372 ------------ .../01-training-2class-embeddingbag.ipynb | 561 ++++++++++++++++++ .../production/full-paragraph/pipeline.json | 9 + uv.lock | 2 +- 9 files changed, 980 insertions(+), 602 deletions(-) delete mode 100644 aymurai/models/decision/conv1d.py create mode 100644 aymurai/models/decision/embeddingbag.py delete mode 100644 aymurai/models/decision/tokenizer.py delete mode 100644 aymurai/text/tokenizers/__init__.py delete mode 100644 aymurai/text/tokenizers/spanish.py create mode 100644 notebooks/experiments/decision/01-training-2class-embeddingbag.ipynb diff --git a/aymurai/models/decision/binregex.py b/aymurai/models/decision/binregex.py index a3b77a01..9c6b0a61 100644 --- a/aymurai/models/decision/binregex.py +++ b/aymurai/models/decision/binregex.py @@ -1,96 +1,113 @@ +from __future__ import annotations + import os -import shutil from copy import deepcopy import regex import torch -from unidecode import unidecode from aymurai.logger import get_logger from aymurai.meta.api_interfaces import DocLabel, EntityAttributes from aymurai.meta.pipeline_interfaces import TrainModule from aymurai.meta.types import DataBlock, DataItem -from aymurai.models.decision.conv1d import Conv1dTextClassifier -from aymurai.models.decision.tokenizer import Tokenizer +from aymurai.models.decision.embeddingbag import ( + TinyEmbeddingBagClassifier, + encode_text, + make_offsets, +) from aymurai.utils.download import download from aymurai.utils.misc import get_element, is_url logger = get_logger(__name__) -class DecisionConv1dBinRegex(TrainModule): +class DecisionEmbeddingBagBinRegex(TrainModule): def __init__( self, - tokenizer_path: str, model_checkpoint: str, device: str = "cpu", threshold: float = 0.88, return_only_with_detalle: bool = True, ): self._device = device - self._tokenizer_path = tokenizer_path self._model_path = model_checkpoint self.threshold = threshold self.return_only_with_detalle = return_only_with_detalle - # download if needed - # tokenizer basepath = os.getenv("AYMURAI_CACHE_BASEPATH", "/resources/cache/aymurai") - if is_url(url := self._tokenizer_path): - output = f"{basepath}/{self.__name__}/tokenizer.pth" - logger.info(f"downloading tokenizer on {output}") - os.makedirs(os.path.dirname(output), exist_ok=True) - self._tokenizer_path = download(url, output=output) - # model if is_url(url := self._model_path): - output = f"{basepath}/{self.__name__}/model.ckpt" + # Determine file extension from URL or default to safetensors + if url.endswith(".pt"): + ext = ".pt" + else: + ext = ".safetensors" + output = f"{basepath}/{self.__class__.__name__}/model{ext}" logger.info(f"downloading model on {output}") os.makedirs(os.path.dirname(output), exist_ok=True) self._model_path = download(url, output=output) - - self.tokenizer = Tokenizer.load(self._tokenizer_path) - self.model = Conv1dTextClassifier.load_from_checkpoint( + # If downloading safetensors, also try to download config + if ext == ".safetensors": + config_url = url.rsplit(".safetensors", 1)[0] + ".json" + config_output = output.rsplit(".safetensors", 1)[0] + ".json" + try: + download(config_url, output=config_output) + except Exception as e: + logger.warning(f"Could not download config file: {e}") + + self.model, self.cfg = TinyEmbeddingBagClassifier.from_checkpoint( self._model_path, - map_location=self._device, + device=self._device, ) self.model = self.model.eval() - def save(self, basepath: str) -> dict | None: - # save tokenizer - os.makedirs(basepath, exist_ok=True) - self._tokenizer_path = f"{basepath}/tokenizer.pth" - self.tokenizer.save(self._tokenizer_path) - logger.info(f"tokenizer saved on: {self._tokenizer_path}") - - # save model - new_model_path = f"{basepath}/model.ckpt" - shutil.copy(self._model_path, new_model_path) - self._model_path = new_model_path - logger.info(f"model saved on: {self._model_path}") - return { - "tokenizer_path": self._tokenizer_path, - "model_checkpoint": self._model_path, - "device": self._device, - } - - @classmethod - def load(cls, path: str, **kwargs): - return cls( - tokenizer_path=f"{path}/tokenizer.pth", - model_checkpoint=f"{path}/model.ckpt", - **kwargs, - ) + def fit(self, train: DataBlock, val: DataBlock) -> None: + """ + Fit the model on training data. Currently not implemented. - def fit(self, train: DataBlock, val: DataBlock): + Args: + train (DataBlock): Training data block. + val (DataBlock): Validation data block. + """ logger.warning("fit routine not implemented") pass def predict(self, data: DataBlock) -> DataBlock: + """ + Predict on a data block. + + Args: + data (DataBlock): Input data block. + + Returns: + DataBlock: Predicted data block. + """ # FIXME: optimize - logger.warn("predict not optimized") + logger.warning("predict not optimized") return [self.predict_single(item) for item in data] - def get_subcategory(self, text): + def model_input_from_text(self, text: str) -> tuple[torch.Tensor, torch.Tensor]: + """ + Convert text to model input tensors. + + Args: + text (str): Input text. + + Returns: + tuple[torch.Tensor, torch.Tensor]: (flat_tokens, offsets) for model input. + """ + token_ids = encode_text(text, self.cfg).to(self._device) + return make_offsets([token_ids]) + + def get_subcategory(self, text: str) -> list[str]: + """ + Determine the subcategory of a decision based on its text. + + Args: + text (str): Text of the decision. + + Returns: + list[str]: List of subcategories for the decision. + """ pattern_no_hace_lugar = regex.compile( r"(?i)(no hacer? lugar|rechaz[ao]r?|no admitir|no convalidar|no autorizar|declarar inadmisible)" ) @@ -100,8 +117,19 @@ def get_subcategory(self, text): else: return ["hace_lugar"] - def gen_aymurai_entity(self, text: str, category: int, score: float): + def gen_aymurai_entity(self, text: str, score: float) -> dict: + """ + Generate an Aymurai entity dictionary for a decision. + + Args: + text (str): Text of the decision. + score (float): Confidence score of the decision. + + Returns: + dict: Aymurai entity dictionary. + """ subcategory = self.get_subcategory(text) + attrs = EntityAttributes( aymurai_label="DECISION", aymurai_label_subclass=subcategory, @@ -119,18 +147,27 @@ def gen_aymurai_entity(self, text: str, category: int, score: float): ent["label"] = "DECISION" ent["context_pre"] = "" ent["context_post"] = "" + return ent def predict_single(self, item: DataItem) -> DataItem: + """ + Predict a single data item. + + Args: + item (DataItem): The data item to predict. + + Returns: + DataItem: The predicted data item with added entities if applicable. + """ item = deepcopy(item) text = item["data"]["doc.text"] - text = unidecode(text) - input_ids = self.tokenizer.encode_batch([text]).to(self.model.device) + flat_tokens, offsets = self.model_input_from_text(text) with torch.no_grad(): - log_prob = self.model(input_ids).exp() - # using category 1 as global score (binary) - prob = log_prob.detach().numpy()[0, 1] + logits = self.model(flat_tokens, offsets) + probs = logits.softmax(dim=1).cpu() + prob = float(probs[0, 1]) category = int(prob > self.threshold) score = prob @@ -143,7 +180,7 @@ def predict_single(self, item: DataItem) -> DataItem: if self.return_only_with_detalle and not detalles: return item - ent = self.gen_aymurai_entity(text=text, category=category, score=score) + ent = self.gen_aymurai_entity(text=text, score=score) ents.append(ent) if "predictions" not in item: @@ -152,3 +189,53 @@ def predict_single(self, item: DataItem) -> DataItem: item["predictions"]["entities"] = ents return item + + def save(self, basepath: str) -> dict: + """ + Save the model to a directory. + + Args: + basepath (str): Directory path to save the model. + + Returns: + dict: A dictionary containing metadata about the saved model. + """ + os.makedirs(basepath, exist_ok=True) + + # Use safetensors as the main format + new_model_path = f"{basepath}/model.safetensors" + self.model.save_checkpoint(new_model_path, use_safetensors=True) + self._model_path = new_model_path + logger.info(f"model saved on: {self._model_path}") + + return { + "model_checkpoint": self._model_path, + "device": self._device, + "threshold": self.threshold, + "return_only_with_detalle": self.return_only_with_detalle, + } + + @classmethod + def load(cls, path: str, **kwargs) -> DecisionEmbeddingBagBinRegex: + """ + Load a DecisionEmbeddingBagBinRegex model from a directory. + + Args: + path (str): Path to the directory containing the model files. + + Returns: + DecisionEmbeddingBagBinRegex: The loaded model instance. + """ + # Try safetensors first, then .pt + safetensors_path = f"{path}/model.safetensors" + pt_path = f"{path}/model.pt" + + if os.path.exists(safetensors_path): + model_checkpoint = safetensors_path + elif os.path.exists(pt_path): + model_checkpoint = pt_path + else: + # Fallback to .pt for backward compatibility + model_checkpoint = pt_path + + return cls(model_checkpoint=model_checkpoint, **kwargs) diff --git a/aymurai/models/decision/conv1d.py b/aymurai/models/decision/conv1d.py deleted file mode 100644 index 9c93dfad..00000000 --- a/aymurai/models/decision/conv1d.py +++ /dev/null @@ -1,138 +0,0 @@ -import pytorch_lightning as pl -import torch -import torch.nn.functional as F -import torchmetrics -from torch import nn -from torch.optim.lr_scheduler import ReduceLROnPlateau - - -class Conv1dTextClassifier(pl.LightningModule): - def __init__( - self, - vocab_size: int, - max_tokens: int = 128, - embed_len: int = 128, - nfeatures: int = 64, - num_classes: int = 2, - lr_scheduler_patience: int = 2, - class_weights: list = [], - ): - self.vocab_size = vocab_size - self.max_tokens = max_tokens - self.embed_len = embed_len - self.nfeatures = nfeatures - self.num_classes = num_classes - self.lr = 1e-3 - self.lr_scheduler_patience = lr_scheduler_patience - self.class_weights = class_weights - - super().__init__() - self.save_hyperparameters() - - # layers - self.embedding_layer = nn.Embedding( - num_embeddings=self.vocab_size, - embedding_dim=self.embed_len, - ) - self.conv1 = nn.Conv1d(self.embed_len, 64, kernel_size=7, padding="same") - self.conv2 = nn.Conv1d(64, 32, kernel_size=7, padding="same") - self.pooling = nn.MaxPool1d(2) - - self.linear1 = nn.Linear(32, 32) - self.linear2 = nn.Linear(32, self.num_classes) - # self.linear = nn.Linear(self.nfeatures, self.num_classes) - - if len(self.class_weights): - self.class_weights = torch.tensor(class_weights, dtype=torch.float32) - - self.logsoftmax = nn.LogSoftmax(dim=1) - self.loss = nn.NLLLoss(weight=self.class_weights) - - # metrics - self.accuracy = torchmetrics.Accuracy( - task="multiclass", - num_classes=self.num_classes, - ) - self.f1score = torchmetrics.F1Score( - task="multiclass", - num_classes=self.num_classes, - ) - - def forward(self, X_batch): - x = self.embedding_layer(X_batch) - x = x.reshape( - len(x), self.embed_len, self.max_tokens - ) ## Embedding Length needs to be treated as channel dimension - x = F.relu(self.conv1(x)) - x = self.pooling(x) - # x = F.dropout(x, 0.5) - x = F.relu(self.conv2(x)) - x, _ = x.max(dim=-1) - - x = self.linear2(x) - x = self.logsoftmax(x) - - return x - - def predict_step(self, batch, batch_idx): - return self(batch) - - def training_step(self, batch, batch_idx): - # training_step defines the train loop. - x, y = batch - - y_pred = self.forward(x) - - loss = self.loss(y_pred, y) - acc = self.accuracy(y_pred, y) - f1score = self.f1score(y_pred, y) - - self.log("loss", loss, on_epoch=True, prog_bar=True, logger=True) - self.log("acc", acc, on_epoch=True, prog_bar=True, logger=True) - self.log("f1score", f1score, on_epoch=True, prog_bar=True, logger=True) - return loss - - def validation_step(self, batch, batch_idx): - x, y = batch - - y_pred = self.forward(x) - - # loss = F.cross_entropy(y_pred, y) - loss = self.loss(y_pred, y) - acc = self.accuracy(y_pred, y) - f1score = self.f1score(y_pred, y) - - self.log("val_loss", loss, on_epoch=True, prog_bar=True, logger=True) - self.log("val_acc", acc, on_epoch=True, prog_bar=True, logger=True) - self.log("val_f1score", f1score, on_epoch=True, prog_bar=True, logger=True) - - def test_step(self, batch, batch_idx): - x, y = batch - - y_pred = self.forward(x) - - # loss = F.cross_entropy(y_pred, y) - loss = self.loss(y_pred, y) - acc = self.accuracy(y_pred, y) - f1score = self.f1score(y_pred, y) - - self.log("test_loss", loss, on_epoch=True, prog_bar=True, logger=True) - self.log("test_acc", acc, on_epoch=True, prog_bar=True, logger=True) - self.log("test_f1score", f1score, on_epoch=True, prog_bar=True, logger=True) - - def configure_optimizers(self): - # optimizer = torch.optim.Adam(self.parameters(), lr=1e-3) - optimizer = torch.optim.AdamW(self.parameters(), lr=self.lr) - return { - "optimizer": optimizer, - "lr_scheduler": { - "scheduler": ReduceLROnPlateau( - optimizer, - patience=self.lr_scheduler_patience, - ), - "monitor": "val_loss", - "frequency": 1, - # If "monitor" references validation metrics, then "frequency" should be set to a - # multiple of "trainer.check_val_every_n_epoch". - }, - } diff --git a/aymurai/models/decision/embeddingbag.py b/aymurai/models/decision/embeddingbag.py new file mode 100644 index 00000000..19e29d46 --- /dev/null +++ b/aymurai/models/decision/embeddingbag.py @@ -0,0 +1,267 @@ +from __future__ import annotations + +import hashlib +import json +import os +import re +from dataclasses import dataclass, field + +import torch +from safetensors.torch import load_file as safe_load_file +from safetensors.torch import save_file as safe_save_file +from torch import nn +from unidecode import unidecode + + +def _normalize_text(text: str) -> str: + """ + Normalize text by removing accents, converting to lowercase, and collapsing whitespace. + + Args: + text (str): The input text to normalize. + + Returns: + str: The normalized text. + """ + text = unidecode(str(text)).lower() + text = re.sub(r"\s+", " ", text).strip() + return text + + +def _tokenize(text: str) -> list[str]: + """ + Tokenize text by splitting on spaces after normalization. + + Args: + text (str): The input text to tokenize. + + Returns: + list[str]: The list of tokens. + """ + text = _normalize_text(text) + return [token for token in text.split(" ") if token] + + +def _hash_token(token: str, vocab_size: int) -> int: + """ + Hash a token string into an integer in the range [0, vocab_size). + + Args: + token (str): The token string to hash. + vocab_size (int): The size of the vocabulary. + + Returns: + int: The hashed token id. + """ + digest = hashlib.blake2b(token.encode("utf-8"), digest_size=4).digest() + return int.from_bytes(digest, "little") % vocab_size + + +@dataclass +class EmbeddingBagConfig: + vocab_size: int = 20000 + embed_dim: int = 64 + max_tokens: int = 128 + dropout: float = 0.1 + num_classes: int = 2 + # allow passthrough of unknown config keys when loading from checkpoints + extra: dict = field(default_factory=dict) + + +def _merge_config(ckpt_cfg: dict | None) -> EmbeddingBagConfig: + """ + Merge checkpoint config with defaults, stashing unknown keys in extra. + + Args: + ckpt_cfg (dict | None): The config dictionary from checkpoint. + + Returns: + EmbeddingBagConfig: The merged configuration object. + """ + ckpt_cfg = ckpt_cfg or {} + cfg_defaults = EmbeddingBagConfig().__dict__ + merged = {**cfg_defaults} + extra = {} + + for k, v in ckpt_cfg.items(): + if k in cfg_defaults: + merged[k] = v + else: + extra[k] = v + + merged["extra"] = extra + + return EmbeddingBagConfig(**merged) + + +def encode_text(text: str, cfg: EmbeddingBagConfig) -> torch.Tensor: + """ + Encode text into token ids using hashing; truncates to cfg.max_tokens. + + Args: + text (str): The input text to encode. + cfg (EmbeddingBagConfig): The configuration with vocab size and max tokens. + + Returns: + torch.Tensor: The tensor of token ids. + """ + tokens = _tokenize(text) + token_ids = [_hash_token(tok, cfg.vocab_size) for tok in tokens[: cfg.max_tokens]] + + if not token_ids: + token_ids = [0] + + return torch.tensor(token_ids, dtype=torch.long) + + +def make_offsets(token_seqs: list[torch.Tensor]) -> tuple[torch.Tensor, torch.Tensor]: + """ + Flatten variable-length token sequences for EmbeddingBag. + + Args: + token_seqs (list[torch.Tensor]): List of 1D tensors of token ids. + + Returns: + tuple[torch.Tensor, torch.Tensor]: (flat_tokens, offsets) where flat_tokens is a 1D tensor of concatenated tokens, + and offsets is a 1D tensor indicating start indices of each sequence. + """ + device = token_seqs[0].device if token_seqs else None + offsets = torch.zeros(len(token_seqs), dtype=torch.long, device=device) + flat = [] + total = 0 + + for i, seq in enumerate(token_seqs): + offsets[i] = total + flat.append(seq) + total += len(seq) + flat_tokens = torch.cat(flat) if flat else torch.tensor([], device=device) + + return flat_tokens, offsets + + +class TinyEmbeddingBagClassifier(nn.Module): + def __init__(self, cfg: EmbeddingBagConfig): + super().__init__() + self.cfg = cfg + self.embedding = nn.EmbeddingBag(cfg.vocab_size, cfg.embed_dim, mode="mean") + self.dropout = nn.Dropout(cfg.dropout) + self.head = nn.Linear(cfg.embed_dim, cfg.num_classes) + + def forward(self, tokens: torch.Tensor, offsets: torch.Tensor) -> torch.Tensor: + """ + Forward pass of the TinyEmbeddingBagClassifier. + + Args: + tokens (torch.Tensor): Tensor of token ids. + offsets (torch.Tensor): Tensor of offsets for EmbeddingBag. + + Returns: + torch.Tensor: The output logits for each class. + """ + x = self.embedding(tokens, offsets) + x = self.dropout(x) + return self.head(x) + + @classmethod + def from_checkpoint( + cls, checkpoint_path: str, device: str = "cpu" + ) -> tuple[TinyEmbeddingBagClassifier, EmbeddingBagConfig]: + """ + Load a TinyEmbeddingBagClassifier from a checkpoint file. + + Raises: + ImportError: If safetensors is required but not installed. + + Returns: + tuple[TinyEmbeddingBagClassifier, EmbeddingBagConfig]: The loaded model and its configuration. + """ + if checkpoint_path.endswith(".safetensors"): + if safe_load_file is None: + raise ImportError( + "safetensors is not installed; cannot load .safetensors checkpoint" + ) + state = safe_load_file(checkpoint_path, device=device) + cfg_path = os.path.splitext(checkpoint_path)[0] + ".json" + cfg_data = {} + if os.path.exists(cfg_path): + with open(cfg_path, "r") as f: + cfg_data = json.load(f) + cfg = _merge_config(cfg_data) + else: + checkpoint = torch.load(checkpoint_path, map_location=device) + cfg = _merge_config(checkpoint.get("config", {})) + state = checkpoint.get("state_dict", checkpoint) + + model = cls(cfg) + model.load_state_dict(state) + model.to(device) + model.eval() + return model, cfg + + def save_checkpoint( + self, + save_path: str, + use_safetensors: bool = True, + ) -> str: + """ + Save model checkpoint. + + Args: + save_path (str): Path to save checkpoint. Extension determines format. + If no extension, .safetensors or .pt will be added based on use_safetensors. + use_safetensors (bool): If True and no extension provided, save as .safetensors. + If False, save as .pt format. Defaults to True. + + Returns: + str: The actual path where the checkpoint was saved. + """ + # Determine save format based on extension or flag + if not save_path.endswith((".safetensors", ".pt")): + ext = ".safetensors" if use_safetensors else ".pt" + save_path = save_path + ext + + os.makedirs(os.path.dirname(save_path) or ".", exist_ok=True) + + if save_path.endswith(".safetensors"): + if safe_save_file is None: + raise ImportError( + "safetensors is not installed; cannot save .safetensors checkpoint. " + "Install with: pip install safetensors" + ) + # Save model weights as safetensors + safe_save_file(self.state_dict(), save_path) + + # Save config as JSON + cfg_path = os.path.splitext(save_path)[0] + ".json" + + with open(cfg_path, "w") as f: + json.dump( + { + "vocab_size": self.cfg.vocab_size, + "embed_dim": self.cfg.embed_dim, + "max_tokens": self.cfg.max_tokens, + "dropout": self.cfg.dropout, + "num_classes": self.cfg.num_classes, + **self.cfg.extra, + }, + f, + indent=2, + ) + else: + # Save as PyTorch checkpoint with config embedded + torch.save( + { + "state_dict": self.state_dict(), + "config": { + "vocab_size": self.cfg.vocab_size, + "embed_dim": self.cfg.embed_dim, + "max_tokens": self.cfg.max_tokens, + "dropout": self.cfg.dropout, + "num_classes": self.cfg.num_classes, + **self.cfg.extra, + }, + }, + save_path, + ) + + return save_path diff --git a/aymurai/models/decision/tokenizer.py b/aymurai/models/decision/tokenizer.py deleted file mode 100644 index bbd5cae4..00000000 --- a/aymurai/models/decision/tokenizer.py +++ /dev/null @@ -1,36 +0,0 @@ -import torch -from unidecode import unidecode - -from aymurai.text.tokenizers.spanish import SpanishTokenizer - - -class Tokenizer(object): - def __init__(self, vocab): - self.max_len = 128 - self.tokenizer = SpanishTokenizer() - self.vocab = vocab - - def save(self, path: str): - torch.save(self.vocab, path) - - @classmethod - def load(cls, path: str): - vocab = torch.load(path, weights_only=False) - return cls(vocab=vocab) - - def __call__(self, text: str): - text = text.lower() - text = unidecode(text) - return self.tokenizer(text) - - def encode(self, text: int): - tokens = self(text)[: self.max_len] - indices = self.vocab(tokens) - indices = torch.tensor(indices, dtype=torch.int64) - indices = torch.nn.functional.pad(indices, (0, self.max_len - len(indices))) - return indices - - def encode_batch(self, texts): - indices = [self.encode(text) for text in texts] - indices = torch.stack(indices) - return indices diff --git a/aymurai/text/tokenizers/__init__.py b/aymurai/text/tokenizers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/aymurai/text/tokenizers/spanish.py b/aymurai/text/tokenizers/spanish.py deleted file mode 100644 index fa11b0b4..00000000 --- a/aymurai/text/tokenizers/spanish.py +++ /dev/null @@ -1,372 +0,0 @@ -""" -A simplified clone of spaCy’s Spanish tokenizer (and related components) -without any dependency on spaCy. This module provides: - • TOKENIZER_EXCEPTIONS – a mapping for known exceptions. - • STOP_WORDS – Spanish stop words. - • TOKENIZER_SUFFIXES and TOKENIZER_INFIXES – basic regex rules. - • Lexical attribute getter for “like_num”. - • SpanishTokenizer – a class that tokenizes text using the above rules. - -Note: This is a “simple but close” clone. spaCy’s internal rules are very -comprehensive; here we implement only some of the core ideas. -""" - -import re - -# --- Definitions used in spaCy’s internals, simplified --- -ORTH = "ORTH" -NORM = "NORM" - - -def update_exc(base, new_exc): - base.update(new_exc) - return base - - -# A base (empty) exceptions dict -BASE_EXCEPTIONS = {} - -# --- Tokenizer Exceptions (from spacy.lang.es.tokenizer_exceptions) --- -_exc = { - "pal": [{ORTH: "pa"}, {ORTH: "l", NORM: "el"}], -} - -for exc_data in [ - {ORTH: "n°"}, - {ORTH: "°C"}, - {ORTH: "aprox."}, - {ORTH: "dna."}, - {ORTH: "dpto."}, - {ORTH: "ej."}, - {ORTH: "esq."}, - {ORTH: "pág."}, - {ORTH: "p.ej."}, - {ORTH: "Ud.", NORM: "usted"}, - {ORTH: "Vd.", NORM: "usted"}, - {ORTH: "Uds.", NORM: "ustedes"}, - {ORTH: "Vds.", NORM: "ustedes"}, - {ORTH: "vol.", NORM: "volúmen"}, -]: - _exc[exc_data[ORTH]] = [exc_data] - -# Times exceptions: “12m.” splits into “12” and “m.”, and also the h+a.m./p.m. forms. -_exc["12m."] = [{ORTH: "12"}, {ORTH: "m."}] - -for h in range(1, 13): - for period in ["a.m.", "am"]: - _exc[f"{h}{period}"] = [{ORTH: f"{h}"}, {ORTH: period}] - for period in ["p.m.", "pm"]: - _exc[f"{h}{period}"] = [{ORTH: f"{h}"}, {ORTH: period}] - -for orth in [ - "a.C.", - "a.J.C.", - "d.C.", - "d.J.C.", - "apdo.", - "Av.", - "Avda.", - "Cía.", - "Dr.", - "Dra.", - "EE.UU.", - "Ee.Uu.", - "EE. UU.", - "Ee. Uu.", - "etc.", - "fig.", - "Gob.", - "Gral.", - "Ing.", - "J.C.", - "km/h", - "Lic.", - "m.n.", - "núm.", - "P.D.", - "Prof.", - "Profa.", - "q.e.p.d.", - "Q.E.P.D.", - "S.A.", - "S.L.", - "S.R.L.", - "s.s.s.", - "Sr.", - "Sra.", - "Srta.", -]: - _exc[orth] = [{ORTH: orth}] - -TOKENIZER_EXCEPTIONS = update_exc(BASE_EXCEPTIONS.copy(), _exc) - -# --- Stop words (from spacy.lang.es.stop_words) --- -STOP_WORDS = set( - """ -a acuerdo adelante ademas además afirmó agregó ahi ahora ahí al algo alguna -algunas alguno algunos algún alli allí alrededor ambos ante anterior antes -apenas aproximadamente aquel aquella aquellas aquello aquellos aqui aquél -aquélla aquéllas aquéllos aquí arriba aseguró asi así atras aun aunque añadió -aún - -bajo bastante bien breve buen buena buenas bueno buenos - -cada casi cierta ciertas cierto ciertos cinco claro comentó como con conmigo -conocer conseguimos conseguir considera consideró consigo consigue consiguen -consigues contigo contra creo cual cuales cualquier cuando cuanta cuantas -cuanto cuantos cuatro cuenta cuál cuáles cuándo cuánta cuántas cuánto cuántos -cómo - -da dado dan dar de debajo debe deben debido decir dejó del delante demasiado -demás dentro deprisa desde despacio despues después detras detrás dia dias dice -dicen dicho dieron diez diferente diferentes dijeron dijo dio doce donde dos -durante día días dónde - -e el ella ellas ello ellos embargo en encima encuentra enfrente enseguida -entonces entre era eramos eran eras eres es esa esas ese eso esos esta estaba -estaban estado estados estais estamos estan estar estará estas este esto estos -estoy estuvo está están excepto existe existen explicó expresó él ésa ésas ése -ésos ésta éstas éste éstos - -fin final fue fuera fueron fui fuimos - -gran grande grandes - -ha haber habia habla hablan habrá había habían hace haceis hacemos hacen hacer -hacerlo haces hacia haciendo hago han hasta hay haya he hecho hemos hicieron -hizo hoy hubo - -igual incluso indicó informo informó ir - -junto - -la lado largo las le les llegó lleva llevar lo los luego - -mal manera manifestó mas mayor me mediante medio mejor mencionó menos menudo mi -mia mias mientras mio mios mis misma mismas mismo mismos modo mucha muchas -mucho muchos muy más mí mía mías mío míos - -nada nadie ni ninguna ningunas ninguno ningunos ningún no nos nosotras nosotros -nuestra nuestras nuestro nuestros nueva nuevas nueve nuevo nuevos nunca - -o ocho once os otra otras otro otros - -para parece parte partir pasada pasado paìs peor pero pesar poca pocas poco -pocos podeis podemos poder podria podriais podriamos podrian podrias podrá -podrán podría podrían poner por porque posible primer primera primero primeros -pronto propia propias propio propios proximo próximo próximos pudo pueda puede -pueden puedo pues - -qeu que quedó queremos quien quienes quiere quiza quizas quizá quizás quién -quiénes qué - -realizado realizar realizó repente respecto - -sabe sabeis sabemos saben saber sabes salvo se sea sean segun segunda segundo -según seis ser sera será serán sería señaló si sido siempre siendo siete sigue -siguiente sin sino sobre sois sola solamente solas solo solos somos son soy su -supuesto sus suya suyas suyo suyos sé sí sólo - -tal tambien también tampoco tan tanto tarde te temprano tendrá tendrán teneis -tenemos tener tenga tengo tenido tenía tercera tercero ti tiene tienen toda -todas todavia todavía todo todos total tras trata través tres tu tus tuvo tuya -tuyas tuyo tuyos tú - -u ultimo un una unas uno unos usa usais usamos usan usar usas uso usted ustedes -última últimas último últimos - -va vais vamos van varias varios vaya veces ver verdad verdadera verdadero vez -vosotras vosotros voy vuestra vuestras vuestro vuestros - -y ya yo -""".split() -) - -# --- Punctuation-related rules (a simplified version of spacy.lang.es.punctuation) --- -# Here we provide basic suffix and infix patterns. -TOKENIZER_SUFFIXES = [ - r"[—–]", # dashes - r"[.,!?;:%]", # common punctuation - r"['\"“”‘’]", # quotes -] -# Infixes that split on, for example, hyphens between digits. -TOKENIZER_INFIXES = [ - r"(?<=[0-9])[-/](?=[0-9])", -] - -# --- Lexical attribute getter (from spacy.lang.es.lex_attrs) --- -_num_words = { - "cero", - "uno", - "dos", - "tres", - "cuatro", - "cinco", - "seis", - "siete", - "ocho", - "nueve", - "diez", - "once", - "doce", - "trece", - "catorce", - "quince", - "dieciséis", - "diecisiete", - "dieciocho", - "diecinueve", - "veinte", - "veintiuno", - "veintidós", - "veintitrés", - "veinticuatro", - "veinticinco", - "veintiséis", - "veintisiete", - "veintiocho", - "veintinueve", - "treinta", - "cuarenta", - "cincuenta", - "sesenta", - "setenta", - "ochenta", - "noventa", - "cien", - "mil", - "millón", - "billón", - "trillón", -} -_ordinal_words = { - "primero", - "segundo", - "tercero", - "cuarto", - "quinto", - "sexto", - "séptimo", - "octavo", - "noveno", - "décimo", - "undécimo", - "duodécimo", - "decimotercero", - "decimocuarto", - "decimoquinto", - "decimosexto", - "decimoséptimo", - "decimoctavo", - "decimonoveno", - "vigésimo", - "trigésimo", - "cuadragésimo", - "quincuagésimo", - "sexagésimo", - "septuagésimo", - "octogésima", - "nonagésima", - "centésima", - "milésima", - "millonésima", - "billonésima", -} - - -def like_num(text): - # Remove a leading +, -, ±, or ~ if present. - if text.startswith(("+", "-", "±", "~")): - text = text[1:] - # Remove commas and periods for a digit check. - text_clean = text.replace(",", "").replace(".", "") - if text_clean.isdigit(): - return True - if text.count("/") == 1: - num, denom = text.split("/") - if num.isdigit() and denom.isdigit(): - return True - text_lower = text.lower() - if text_lower in _num_words: - return True - if text_lower in _ordinal_words: - return True - return False - - -LEX_ATTRS = {"LIKE_NUM": like_num} - - -# --- Spanish Tokenizer Implementation --- -class SpanishTokenizer: - """ - Spacy spanish tokenizer clone - Source: https://github.com/explosion/spaCy/tree/b3c46c315eb16ce644bddd106d31c3dd349f6bb2/spacy/lang/es - """ - - def __init__(self): - # A basic regex for “words” and “non-space” characters. - self.word_re = re.compile(r"\w+|[^\w\s]", re.UNICODE) - # Combine infix patterns into one regex. - self.infix_re = re.compile("|".join(TOKENIZER_INFIXES)) - - def apply_exceptions(self, text: str): - """ - If the given text exactly matches a tokenizer exception, - return the corresponding token list. - """ - if text in TOKENIZER_EXCEPTIONS: - # For each exception item (a dict), return the ORTH value, - # falling back to NORM if available. - return [ - item.get(ORTH, item.get(NORM, item)) - for item in TOKENIZER_EXCEPTIONS[text] - ] - return None - - def tokenize(self, text: str): - """ - Tokenizes the input text into a list of tokens. - The algorithm first splits on whitespace, checks for exceptions, - and otherwise applies regex-based splitting and infix splitting. - """ - tokens = [] - for word in text.split(): - exc = self.apply_exceptions(word) - if exc is not None: - tokens.extend(exc) - else: - # First, use regex to split word into basic tokens. - sub_tokens = self.word_re.findall(word) - final_tokens = [] - for token in sub_tokens: - # Apply infix splitting if applicable. - splits = self.infix_re.split(token) - if len(splits) > 1: - last_end = 0 - # Re-find infix matches to capture separators. - for m in self.infix_re.finditer(token): - pre = token[last_end : m.start()] - sep = token[m.start() : m.end()] - if pre: - final_tokens.append(pre) - final_tokens.append(sep) - last_end = m.end() - remainder = token[last_end:] - if remainder: - final_tokens.append(remainder) - else: - final_tokens.append(token) - tokens.extend(final_tokens) - return tokens - - def __call__(self, text: str): - return self.tokenize(text) - - -# --- Example Usage --- -if __name__ == "__main__": - sample_text = "El Sr. habló con 12a.m. y 3pm en la reunión. ¡Increíble, verdad?" - tokenizer = SpanishTokenizer() - tokens = tokenizer(sample_text) - print("Tokens:", tokens) diff --git a/notebooks/experiments/decision/01-training-2class-embeddingbag.ipynb b/notebooks/experiments/decision/01-training-2class-embeddingbag.ipynb new file mode 100644 index 00000000..7870bdb7 --- /dev/null +++ b/notebooks/experiments/decision/01-training-2class-embeddingbag.ipynb @@ -0,0 +1,561 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "44b15c4f", + "metadata": {}, + "source": [ + "# Tiny decision classifier (EmbeddingBag, no torchtext)\n", + "\n", + "A fastText-style baseline for the decision detection task. It uses a hashed bag of words with `nn.EmbeddingBag` pooling and a single linear head to stay extremely small. No `torchtext` dependencies.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e40eb3f5", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n" + ] + }, + { + "cell_type": "markdown", + "id": "ce1f616a", + "metadata": {}, + "source": [ + "## Imports and config\n", + "\n", + "- Uses deterministic hashing for tokens to keep a fixed vocab size without building a vocab file.\n", + "- Model size is controlled by `vocab_size` and `embed_dim` (for example, 20k vocab and 64-dim embeddings is roughly 1.3M parameters).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "461aa6da", + "metadata": {}, + "outputs": [], + "source": [ + "import hashlib\n", + "import os\n", + "import random\n", + "import re\n", + "from dataclasses import dataclass\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import torch\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.utils.class_weight import compute_class_weight\n", + "from torch import nn\n", + "from torch.cuda.amp import autocast, GradScaler\n", + "from torch.utils.data import DataLoader, Dataset\n", + "from unidecode import unidecode\n", + "\n", + "SEED = 42\n", + "random.seed(SEED)\n", + "np.random.seed(SEED)\n", + "torch.manual_seed(SEED)\n", + "torch.cuda.manual_seed_all(SEED)\n", + "torch.backends.cudnn.deterministic = True\n", + "\n", + "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + "\n", + "\n", + "@dataclass\n", + "class Config:\n", + " data_path: str = \"sentences-decision-manual.csv\"\n", + " vocab_size: int = 20000\n", + " embed_dim: int = 64\n", + " max_tokens: int = 128\n", + " batch_size: int = 512\n", + " lr: float = 5e-3\n", + " weight_decay: float = 1e-3\n", + " epochs: int = 50\n", + " dropout: float = 0.1\n", + " num_workers: int = 2\n", + " checkpoint_path: str = \"checkpoints/tiny-embeddingbag.pt\"\n", + "\n", + "\n", + "cfg = Config()\n", + "\n", + "print(f\"device: {device}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "69fbe089", + "metadata": {}, + "source": [ + "## Load data\n", + "\n", + "Input columns: `path`, `nro_registro`, `tomo`, `sentence`, `decision`, `hace_lugar`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5574c092", + "metadata": {}, + "outputs": [], + "source": [ + "data = pd.read_csv(\n", + " cfg.data_path,\n", + " usecols=[\"path\", \"nro_registro\", \"tomo\", \"sentence\", \"decision\", \"hace_lugar\"],\n", + ")\n", + "print(data.head())\n", + "print(f\"Loaded {len(data)} rows\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3bc71145", + "metadata": {}, + "source": [ + "## Preprocess labels\n", + "\n", + "- Drop null rows and duplicate sentences.\n", + "- Collapse labels to a binary target: `0 = not decision`, `1 = decision` (regardless of `hace_lugar`).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cafdac5c", + "metadata": {}, + "outputs": [], + "source": [ + "data.dropna(inplace=True)\n", + "\n", + "\n", + "def force_bool(value):\n", + " return True if value in [\"True\", True, 1, \"1\"] else False\n", + "\n", + "\n", + "def get_category(row):\n", + " decision, hace_lugar = row\n", + " if not decision:\n", + " return 0\n", + " if decision and not hace_lugar:\n", + " return 1\n", + " if decision and hace_lugar:\n", + " return 1\n", + " raise ValueError(\"unexpected label combo\")\n", + "\n", + "\n", + "data[\"decision\"] = data[\"decision\"].apply(force_bool).astype(bool)\n", + "data[\"hace_lugar\"] = data[\"hace_lugar\"].apply(force_bool).astype(bool)\n", + "data[\"category\"] = data[[\"decision\", \"hace_lugar\"]].apply(get_category, axis=1)\n", + "\n", + "data.dropna(subset=[\"category\"], inplace=True)\n", + "data.drop_duplicates(subset=\"sentence\", inplace=True)\n", + "\n", + "print(f\"After cleaning: {len(data)} rows\")\n", + "print(data[[\"category\"]].value_counts(normalize=True) * 100)\n" + ] + }, + { + "cell_type": "markdown", + "id": "cae1826b", + "metadata": {}, + "source": [ + "## Train/val/test split\n", + "\n", + "80/10/10 with stratification on the binary label.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52cb5244", + "metadata": {}, + "outputs": [], + "source": [ + "train_df, test_df = train_test_split(\n", + " data, test_size=0.2, random_state=SEED, stratify=data[\"category\"]\n", + ")\n", + "test_df, val_df = train_test_split(\n", + " test_df, test_size=0.5, random_state=SEED, stratify=test_df[\"category\"]\n", + ")\n", + "\n", + "for name, df_ in {\"train\": train_df, \"val\": val_df, \"test\": test_df}.items():\n", + " print(\n", + " f\"{name}: {len(df_)} rows | class balance: {df_['category'].value_counts(normalize=True).to_dict()}\"\n", + " )\n" + ] + }, + { + "cell_type": "markdown", + "id": "07ae169f", + "metadata": {}, + "source": [ + "## Tokenization and dataset\n", + "\n", + "- Simple normalize → whitespace split.\n", + "- Deterministic 32-bit Blake2 hash per token to map into `[0, vocab_size)`.\n", + "- Truncate to `max_tokens` to bound memory.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1332f587", + "metadata": {}, + "outputs": [], + "source": [ + "def normalize_text(text: str) -> str:\n", + " text = unidecode(str(text)).lower()\n", + " text = re.sub(r\"\\s+\", \" \", text).strip()\n", + " return text\n", + "\n", + "\n", + "def tokenize(text: str) -> list[str]:\n", + " text = normalize_text(text)\n", + " return [tok for tok in text.split(\" \") if tok]\n", + "\n", + "\n", + "def hash_token(token: str, vocab_size: int) -> int:\n", + " digest = hashlib.blake2b(token.encode(\"utf-8\"), digest_size=4).digest()\n", + " return int.from_bytes(digest, \"little\") % vocab_size\n", + "\n", + "\n", + "def encode_text(text: str, cfg: Config) -> torch.Tensor:\n", + " tokens = tokenize(text)\n", + " token_ids = [hash_token(tok, cfg.vocab_size) for tok in tokens[: cfg.max_tokens]]\n", + " if not token_ids:\n", + " token_ids = [0]\n", + " return torch.tensor(token_ids, dtype=torch.long)\n", + "\n", + "\n", + "class HashedTextDataset(Dataset):\n", + " def __init__(self, frame: pd.DataFrame, cfg: Config):\n", + " self.texts = frame[\"sentence\"].tolist()\n", + " self.labels = frame[\"category\"].astype(int).tolist()\n", + " self.cfg = cfg\n", + "\n", + " def __len__(self):\n", + " return len(self.labels)\n", + "\n", + " def __getitem__(self, idx: int):\n", + " token_ids = encode_text(self.texts[idx], self.cfg)\n", + " label = self.labels[idx]\n", + " return token_ids, label\n", + "\n", + "\n", + "def collate_batch(batch):\n", + " token_seqs, labels = zip(*batch)\n", + " offsets = torch.zeros(len(batch), dtype=torch.long)\n", + " total = 0\n", + " flat_tokens = []\n", + " for i, tokens in enumerate(token_seqs):\n", + " offsets[i] = total\n", + " flat_tokens.append(tokens)\n", + " total += len(tokens)\n", + " flat_tokens = torch.cat(flat_tokens)\n", + " labels = torch.tensor(labels, dtype=torch.long)\n", + " return flat_tokens, offsets, labels\n", + "\n", + "\n", + "train_ds = HashedTextDataset(train_df, cfg)\n", + "val_ds = HashedTextDataset(val_df, cfg)\n", + "test_ds = HashedTextDataset(test_df, cfg)\n", + "\n", + "train_loader = DataLoader(\n", + " train_ds,\n", + " batch_size=cfg.batch_size,\n", + " shuffle=True,\n", + " collate_fn=collate_batch,\n", + " num_workers=cfg.num_workers,\n", + ")\n", + "val_loader = DataLoader(\n", + " val_ds,\n", + " batch_size=cfg.batch_size,\n", + " shuffle=False,\n", + " collate_fn=collate_batch,\n", + " num_workers=cfg.num_workers,\n", + ")\n", + "test_loader = DataLoader(\n", + " test_ds,\n", + " batch_size=cfg.batch_size,\n", + " shuffle=False,\n", + " collate_fn=collate_batch,\n", + " num_workers=cfg.num_workers,\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "bc80c8af", + "metadata": {}, + "source": [ + "## Model: EmbeddingBag + Linear\n", + "\n", + "Tiny architecture with mean pooling. Dropout is optional for slight regularization.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2605dcc2", + "metadata": {}, + "outputs": [], + "source": [ + "class TinyEmbeddingBagClassifier(nn.Module):\n", + " def __init__(self, cfg: Config, num_classes: int = 2):\n", + " super().__init__()\n", + " self.embedding = nn.EmbeddingBag(cfg.vocab_size, cfg.embed_dim, mode=\"mean\")\n", + " self.dropout = nn.Dropout(cfg.dropout)\n", + " self.head = nn.Linear(cfg.embed_dim, num_classes)\n", + "\n", + " def forward(self, tokens, offsets):\n", + " x = self.embedding(tokens, offsets)\n", + " x = self.dropout(x)\n", + " return self.head(x)\n", + "\n", + "\n", + "model = TinyEmbeddingBagClassifier(cfg).to(device)\n", + "param_count = sum(p.numel() for p in model.parameters())\n", + "print(model)\n", + "print(f\"Parameter count: {param_count:,}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "1a029057", + "metadata": {}, + "source": [ + "## Training utilities\n", + "\n", + "- Class-weighted cross entropy to compensate imbalance.\n", + "- Simple train/val loop with best-model tracking.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8d02447", + "metadata": {}, + "outputs": [], + "source": [ + "class_weights = compute_class_weight(\n", + " class_weight=\"balanced\",\n", + " classes=np.unique(train_df[\"category\"]),\n", + " y=train_df[\"category\"],\n", + ")\n", + "class_weights = torch.tensor(class_weights, dtype=torch.float, device=device)\n", + "\n", + "criterion = nn.CrossEntropyLoss(weight=class_weights)\n", + "optimizer = torch.optim.AdamW(\n", + " model.parameters(), lr=cfg.lr, weight_decay=cfg.weight_decay\n", + ")\n", + "scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n", + " optimizer,\n", + " mode=\"min\",\n", + " factor=0.5,\n", + " patience=1,\n", + " threshold=1e-3,\n", + " min_lr=1e-5,\n", + ")\n", + "scaler = GradScaler(enabled=(device == \"cuda\"))\n", + "\n", + "\n", + "def run_epoch(model, loader, optimizer=None, scaler=None):\n", + " train_mode = optimizer is not None\n", + " model.train() if train_mode else model.eval()\n", + "\n", + " total_loss = 0.0\n", + " total_correct = 0\n", + " total_examples = 0\n", + "\n", + " for tokens, offsets, labels in loader:\n", + " tokens = tokens.to(device)\n", + " offsets = offsets.to(device)\n", + " labels = labels.to(device)\n", + "\n", + " if train_mode:\n", + " optimizer.zero_grad(set_to_none=True)\n", + " with autocast(enabled=device == \"cuda\"):\n", + " logits = model(tokens, offsets)\n", + " loss = criterion(logits, labels)\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + " else:\n", + " with torch.no_grad(), autocast(enabled=device == \"cuda\"):\n", + " logits = model(tokens, offsets)\n", + " loss = criterion(logits, labels)\n", + "\n", + " preds = logits.argmax(dim=1)\n", + " total_correct += (preds == labels).sum().item()\n", + " total_loss += loss.item() * labels.size(0)\n", + " total_examples += labels.size(0)\n", + "\n", + " avg_loss = total_loss / total_examples\n", + " acc = total_correct / total_examples\n", + " return avg_loss, acc\n", + "\n", + "\n", + "best_state = None\n", + "best_val_loss = float(\"inf\")\n", + "\n", + "for epoch in range(cfg.epochs):\n", + " train_loss, train_acc = run_epoch(model, train_loader, optimizer, scaler=scaler)\n", + " val_loss, val_acc = run_epoch(model, val_loader, optimizer=None, scaler=None)\n", + "\n", + " lr_before = optimizer.param_groups[0][\"lr\"]\n", + " scheduler.step(val_loss)\n", + " lr_after = optimizer.param_groups[0][\"lr\"]\n", + "\n", + " if val_loss < best_val_loss:\n", + " best_val_loss = val_loss\n", + " best_state = {k: v.cpu() for k, v in model.state_dict().items()}\n", + "\n", + " if lr_after < lr_before and best_state is not None:\n", + " # resume from best snapshot whenever LR is reduced\n", + " model.load_state_dict(best_state)\n", + "\n", + " cur_lr = optimizer.param_groups[0][\"lr\"]\n", + " print(\n", + " f\"Epoch {epoch + 1:02d} | lr {cur_lr:.2e} | train loss {train_loss:.4f} acc {train_acc:.3f} | \"\n", + " f\"val loss {val_loss:.4f} acc {val_acc:.3f}\"\n", + " )\n", + "\n", + "if best_state is not None:\n", + " model.load_state_dict(best_state)\n", + " print(f\"Loaded best state with val loss={best_val_loss:.4f}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "fb768bda", + "metadata": {}, + "source": [ + "## Evaluation\n", + "\n", + "Compute accuracy and a quick classification report on the held-out splits.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf65d689", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.metrics import classification_report, confusion_matrix\n", + "\n", + "\n", + "def collect_predictions(model, loader):\n", + " model.eval()\n", + " all_preds, all_labels = [], []\n", + " with torch.no_grad():\n", + " for tokens, offsets, labels in loader:\n", + " tokens = tokens.to(device)\n", + " offsets = offsets.to(device)\n", + " logits = model(tokens, offsets)\n", + " preds = logits.argmax(dim=1).cpu().tolist()\n", + " all_preds.extend(preds)\n", + " all_labels.extend(labels.tolist())\n", + " return all_labels, all_preds\n", + "\n", + "\n", + "for split_name, loader in [(\"val\", val_loader), (\"test\", test_loader)]:\n", + " y_true, y_pred = collect_predictions(model, loader)\n", + " print(f\"=== {split_name.upper()} ===\")\n", + " print(classification_report(y_true, y_pred, digits=3))\n", + " print(\"Confusion matrix:\")\n", + " print(confusion_matrix(y_true, y_pred))\n" + ] + }, + { + "cell_type": "markdown", + "id": "97811900", + "metadata": {}, + "source": [ + "## Save checkpoint\n", + "\n", + "Saves a lightweight `state_dict` for later reuse.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57af7004", + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "\n", + "ckpt_path = Path(cfg.checkpoint_path)\n", + "ckpt_path.parent.mkdir(parents=True, exist_ok=True)\n", + "torch.save({\"config\": cfg.__dict__, \"state_dict\": model.state_dict()}, ckpt_path)\n", + "print(f\"Saved to {ckpt_path}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3c93782b", + "metadata": {}, + "source": [ + "## Save as safetensors\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7dc23b22", + "metadata": {}, + "outputs": [], + "source": [ + "from safetensors.torch import save_file\n", + "import json\n", + "\n", + "# Save model weights as safetensors\n", + "safetensor_path = ckpt_path.with_suffix(\".safetensors\")\n", + "save_file(model.state_dict(), safetensor_path)\n", + "\n", + "# Save config separately as JSON\n", + "config_path = ckpt_path.with_suffix(\".json\")\n", + "with open(config_path, \"w\") as f:\n", + " json.dump(cfg.__dict__, f, indent=2)\n", + "\n", + "print(f\"Saved model to {safetensor_path}\")\n", + "print(f\"Saved config to {config_path}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bdbfa386", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/resources/pipelines/production/full-paragraph/pipeline.json b/resources/pipelines/production/full-paragraph/pipeline.json index b5da2f9e..bfba7682 100644 --- a/resources/pipelines/production/full-paragraph/pipeline.json +++ b/resources/pipelines/production/full-paragraph/pipeline.json @@ -14,6 +14,15 @@ "device": "cpu", "use_tokenizer": false } + ], + [ + "aymurai.models.decision.binregex.DecisionEmbeddingBagBinRegex", + { + "model_checkpoint": "https://github.com/AymurAI/backend/releases/download/v2.0.0-alpha.1/tiny-embeddingbag.safetensors", + "device": "cpu", + "threshold": 0.5, + "return_only_with_detalle": true + } ] ], "postprocess": [ diff --git a/uv.lock b/uv.lock index e8a103b2..78db5ef7 100644 --- a/uv.lock +++ b/uv.lock @@ -285,7 +285,7 @@ wheels = [ [[package]] name = "aymurai" -version = "2.0.0a3.dev9" +version = "2.0.0a4.dev1" source = { editable = "." } dependencies = [ { name = "alembic" }, From 90f73690be89c501e181f27786bc9fe3e3f72ab8 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Wed, 7 Jan 2026 14:40:22 +0000 Subject: [PATCH 028/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20TensorFlow=20en?= =?UTF-8?q?vironment=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.common | 3 --- 1 file changed, 3 deletions(-) diff --git a/.env.common b/.env.common index 5760c797..1eec416e 100644 --- a/.env.common +++ b/.env.common @@ -8,6 +8,3 @@ TESSDATA_PREFIX=/usr/local/share/tessdata AYMURAI_RESTRICTED_DOCUMENT_PDFS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO-pdf" AYMURAI_RESTRICTED_DOCUMENT_DOCS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO" - -TF_CPP_MIN_LOG_LEVEL=3 -TFHUB_CACHE_DIR=/resources/cache/tfhub_modules From 0070916454f445029dabc8d65c9b54fe0c9e4743 Mon Sep 17 00:00:00 2001 From: Paolo Donizetti Date: Thu, 8 Jan 2026 12:08:08 -0300 Subject: [PATCH 029/101] Feature/mlfow integration (#66) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add mlflow-based experiments and services (wip) * feat: finalize mlflow experiment runner and artifact logging * feat: add OpenAI ChatGPT extension and update postStartCommand in devcontainer * 📝 Unify disambiguation evaluation notebooks * 📝 Enhance documentation and add type hints across multiple modules * 📌 Update uv.lock * 🔧 Update devcontainer GPU device configuration * 🔧 Change default Python environment manager to venv * 🔧 Add container names for all services in docker-compose.yml * ➖ Remove commented optional dependencies for GPU support in pyproject.toml * 🔧 Increase document request timeout from 30 to 300 seconds in .env.common * 🚚 Changed environment variable names from DOCUMENT_API_BASE_URL and DOCUMENT_REQUEST_TIMEOUT to API_BASE_URL and REQUEST_TIMEOUT * 🔧 Update dependency installation to include 'mlops' group in entrypoint.sh * 🔖 Update aymurai package version to 2.0.0a5.dev8 --- .devcontainer/devcontainer.json | 9 +- .devcontainer/docker-compose.yml | 4 +- .devcontainer/entrypoint.sh | 2 +- .env.common | 9 + .vscode/settings.json | 2 +- Makefile | 16 +- aymurai/evaluation/metrics.py | 8 +- aymurai/experiments/__init__.py | 1 + .../entity_disambiguation/__init__.py | 1 + .../entity_disambiguation/config.py | 176 +++ .../entity_disambiguation/manifest.py | 51 + .../entity_disambiguation/mlflow_logging.py | 111 ++ .../entity_disambiguation/runner.py | 468 ++++++++ aymurai/utils/paths.py | 40 + docker-compose.yml | 85 ++ docs/experiments/README.md | 90 ++ docs/experiments/base.yaml | 50 + .../metrics}/Desarrollo metrica.md | 4 +- .../01-entity-disambiguation.ipynb | 20 +- .../03-disambiguation-evaluation.ipynb | 6 +- ...ation-from-pre-clustered-validations.ipynb | 8 +- .../01-summarization-benchmark.ipynb | 8 +- .../04-dspy-prompt-optimization.ipynb | 8 +- pyproject.toml | 10 +- uv.lock | 1032 +++++++++++------ 25 files changed, 1802 insertions(+), 417 deletions(-) create mode 100644 aymurai/experiments/__init__.py create mode 100644 aymurai/experiments/entity_disambiguation/__init__.py create mode 100644 aymurai/experiments/entity_disambiguation/config.py create mode 100644 aymurai/experiments/entity_disambiguation/manifest.py create mode 100644 aymurai/experiments/entity_disambiguation/mlflow_logging.py create mode 100644 aymurai/experiments/entity_disambiguation/runner.py create mode 100644 aymurai/utils/paths.py create mode 100644 docs/experiments/README.md create mode 100644 docs/experiments/base.yaml rename {notebooks/experiments/entity-disambiguation => docs/metrics}/Desarrollo metrica.md (98%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2ead9957..5aa72ad5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,6 +9,9 @@ "aymurai-devcontainer-gpu" ], "workspaceFolder": "/workspace", + "mounts": [ + "source=codex-data,target=/home/ubuntu/.codex,type=volume" + ], "customizations": { "vscode": { "settings": { @@ -47,9 +50,11 @@ "cweijan.vscode-database-client2", "christian-kohler.path-intellisense", "github.vscode-github-actions", - "seatonjiang.gitmoji-vscode" + "seatonjiang.gitmoji-vscode", + "openai.chatgpt" ] } }, - "postCreateCommand": "bash /home/ubuntu/entrypoint.sh" + "postCreateCommand": "bash /home/ubuntu/entrypoint.sh", + "postStartCommand": "sudo chown -R ubuntu:ubuntu /home/ubuntu/.codex && sudo chmod -R u+rwX /home/ubuntu/.codex" } \ No newline at end of file diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 5e12b20d..9477b892 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -27,7 +27,9 @@ services: resources: reservations: devices: - - capabilities: [ gpu ] + - driver: nvidia + count: all + capabilities: [ gpu ] aymurai-devcontainer: <<: *template diff --git a/.devcontainer/entrypoint.sh b/.devcontainer/entrypoint.sh index 000c3360..214ac9f6 100644 --- a/.devcontainer/entrypoint.sh +++ b/.devcontainer/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh # install dependencies -uv sync --frozen --all-extras +uv sync --frozen --all-extras --group mlops # configure precommit uv run pre-commit install diff --git a/.env.common b/.env.common index 1eec416e..38601115 100644 --- a/.env.common +++ b/.env.common @@ -8,3 +8,12 @@ TESSDATA_PREFIX=/usr/local/share/tessdata AYMURAI_RESTRICTED_DOCUMENT_PDFS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO-pdf" AYMURAI_RESTRICTED_DOCUMENT_DOCS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO" + +API_BASE_URL=http://localhost:8899 +REQUEST_TIMEOUT=300 + +MLFLOW_TRACKING_URI=http://localhost:5000 +MLFLOW_S3_ENDPOINT_URL=http://localhost:9002 +MLFLOW_S3_IGNORE_TLS=true +MLFLOW_EXPERIMENT_NAME=entity-disambiguation +MLFLOW_ARTIFACT_ROOT=s3://mlflow/artifacts diff --git a/.vscode/settings.json b/.vscode/settings.json index 3fa21ad3..dc48662f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python-envs.defaultEnvManager": "ms-python.python:system", + "python-envs.defaultEnvManager": "ms-python.python:venv", "python-envs.pythonProjects": [], "files.associations": { ".csv": "csv", diff --git a/Makefile b/Makefile index d828a9e1..b49053fd 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,6 @@ API_SERVICE ?= aymurai-api # Select which full API service to control (override with API_FULL_SERVICE=aymurai-api-full-gpu) API_FULL_SERVICE ?= aymurai-api-full - api-build: docker compose build $(API_SERVICE) api-run: @@ -115,6 +114,21 @@ endif docker compose up -d --no-recreate $(OLLAMA_SERVICE) docker compose exec $(OLLAMA_SERVICE) ollama rm $(MODEL) +mlops-up: + docker compose up -d postgres minio minio-mc mlflow + +mlops-stop: + docker compose stop mlflow minio minio-mc postgres + +mlops-logs: + docker compose logs -f mlflow + +exp-run: +ifndef CONFIG + $(error CONFIG variable is required, e.g. make exp-run CONFIG=resources/experiments/entity-disambiguation/exp-gemma3-pv1.yaml) +endif + uv run --group mlops -- python -m aymurai.experiments.entity_disambiguation.runner --config $(CONFIG) + stress-test: locust -f locustfile.py --host http://localhost:8899 diff --git a/aymurai/evaluation/metrics.py b/aymurai/evaluation/metrics.py index 30bceca5..e9b2058a 100644 --- a/aymurai/evaluation/metrics.py +++ b/aymurai/evaluation/metrics.py @@ -7,6 +7,7 @@ from aymurai.logger import get_logger from aymurai.utils.json_data import load_json +from aymurai.utils.paths import prediction_filename_for_test, test_id_from_filename logger = get_logger(__name__) @@ -44,6 +45,7 @@ def get_alias_set(entity: dict[str, Any], normalize: bool = True) -> set[str]: optional 'aliases' (list of strings) and 'canonical_text' (string) keys. normalize (bool, optional): If True, normalizes the alias texts using normalize_text function. Defaults to True. + Returns: set[str]: A set of alias strings (normalized if normalize=True) including both explicit aliases and the canonical text. @@ -339,8 +341,8 @@ def evaluate_prediction_directories( results = [] for test_path in sorted(test_dir.glob("*.json")): - prefix = test_path.name[: -len(".json")] - pred_path = preds_dir / f"{prefix}.json" + doc_id = test_id_from_filename(test_path) + pred_path = preds_dir / prediction_filename_for_test(test_path) if not pred_path.exists(): logger.warning(f"Prediction for {test_path.name} was not found; skipping.") @@ -355,7 +357,7 @@ def evaluate_prediction_directories( sim_threshold=sim_threshold, normalize=normalize, ) - results.append((prefix, score, metrics)) + results.append((doc_id, score, metrics)) average_score = ( sum(score for _, score, _ in results) / len(results) diff --git a/aymurai/experiments/__init__.py b/aymurai/experiments/__init__.py new file mode 100644 index 00000000..78e64a4b --- /dev/null +++ b/aymurai/experiments/__init__.py @@ -0,0 +1 @@ +"""Experiment modules and helpers.""" diff --git a/aymurai/experiments/entity_disambiguation/__init__.py b/aymurai/experiments/entity_disambiguation/__init__.py new file mode 100644 index 00000000..17dc9d09 --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/__init__.py @@ -0,0 +1 @@ +"""Entity disambiguation experiment helpers.""" diff --git a/aymurai/experiments/entity_disambiguation/config.py b/aymurai/experiments/entity_disambiguation/config.py new file mode 100644 index 00000000..68f54534 --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/config.py @@ -0,0 +1,176 @@ +import os +from datetime import datetime, timezone +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from aymurai.utils.yaml_data import load_yaml + + +class ExperimentConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + name: str + run_name: str + + +class ModelConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + provider: str + name: str + temperature: float | None = None + max_tokens: int | None = None + + +class PromptSpec(BaseModel): + model_config = ConfigDict(extra="forbid") + + id: str + text: str | None = None + + +class PromptsConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + system: PromptSpec + user: PromptSpec + + +class DataManifestConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + enabled: bool = True + mode: Literal["metadata_only"] = "metadata_only" + + +class DataConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + ground_truth_dir: str + input_dir: str + dataset_id: str | None = None + manifest: DataManifestConfig = DataManifestConfig() + + +class PredictionsConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + output_base_dir: str + dir_name_template: str + + +class MLflowConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + tracking_uri: str + experiment_name: str + + +class LoggingPrivacyConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + log_prompt_text: bool = True + log_data_manifest: bool = False + log_per_doc_scores: bool = True + + +class PerDocScoresConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + id_strategy: str = "anon_filename" + + +class LoggingConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + mlflow: MLflowConfig + privacy: LoggingPrivacyConfig = LoggingPrivacyConfig() + per_doc_scores: PerDocScoresConfig = PerDocScoresConfig() + + +class EvaluationConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + function: str = "evaluate_disambiguation" + weights: dict[str, float] = Field( + default_factory=lambda: { + "w_ent": 0.4, + "w_alias": 0.35, + "w_label": 0.2, + "w_role": 0.05, + } + ) + per_doc_scores: bool = True + sim_threshold: float | None = None + normalize: bool = True + + +class ExperimentRunConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + experiment: ExperimentConfig + model: ModelConfig + prompts: PromptsConfig + data: DataConfig + predictions: PredictionsConfig + logging: LoggingConfig + evaluation: EvaluationConfig + + +def load_experiment_config(path: str) -> ExperimentRunConfig: + """ + Load an experiment configuration from a YAML file. + + Args: + path (str): Path to the YAML configuration file. + + Raises: + ValueError: If the MLflow tracking URI is not set in the environment or configuration. + + Returns: + ExperimentRunConfig: The loaded experiment configuration. + """ + data = load_yaml(path) + tracking_uri = os.getenv("MLFLOW_TRACKING_URI") + if tracking_uri: + logging_cfg = data.setdefault("logging", {}) + mlflow_cfg = logging_cfg.setdefault("mlflow", {}) + mlflow_cfg["tracking_uri"] = tracking_uri + else: + mlflow_cfg = data.get("logging", {}).get("mlflow", {}) + if "tracking_uri" not in mlflow_cfg: + raise ValueError( + "MLFLOW_TRACKING_URI is required but was not set in the environment." + ) + return ExperimentRunConfig.model_validate(data) + + +def render_run_name( + template: str, + model_name: str, + system_id: str, + user_id: str, + *, + timestamp: str | None = None, +) -> str: + """ + Render a run name based on a template and provided identifiers. + + Args: + template (str): The template string for the run name. + model_name (str): The name of the model. + system_id (str): The system identifier. + user_id (str): The user identifier. + timestamp (str | None, optional): The timestamp string. Defaults to None. + + Returns: + str: The rendered run name. + """ + timestamp = timestamp or datetime.now(timezone.utc).strftime("%y%m%d_%H%M") + return template.format( + model=model_name, + system_id=system_id, + user_id=user_id, + timestamp=timestamp, + ) diff --git a/aymurai/experiments/entity_disambiguation/manifest.py b/aymurai/experiments/entity_disambiguation/manifest.py new file mode 100644 index 00000000..2e2c37fa --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/manifest.py @@ -0,0 +1,51 @@ +import hashlib +import json +from dataclasses import dataclass +from pathlib import Path + + +@dataclass(frozen=True) +class DatasetInfo: + dataset_hash: str + dataset_count: int + dataset_bytes_total: int + manifest: dict | None = None + + +def build_metadata_manifest(root_dir: Path) -> DatasetInfo: + """ + Build a metadata manifest for the dataset located at the given root directory. + + Args: + root_dir (Path): The root directory of the dataset. + + Returns: + DatasetInfo: An object containing dataset hash, count, total bytes, and manifest. + """ + files = [] + total_bytes = 0 + + for path in sorted(root_dir.rglob("*")): + if not path.is_file(): + continue + stat = path.stat() + rel_path = path.relative_to(root_dir).as_posix() + files.append( + { + "path": rel_path, + "size_bytes": stat.st_size, + "mtime": int(stat.st_mtime), + } + ) + total_bytes += stat.st_size + + manifest = {"files": files, "summary": {"count": len(files), "bytes": total_bytes}} + manifest_bytes = json.dumps(files, sort_keys=True).encode("utf-8") + dataset_hash = hashlib.sha256(manifest_bytes).hexdigest() + + return DatasetInfo( + dataset_hash=dataset_hash, + dataset_count=len(files), + dataset_bytes_total=total_bytes, + manifest=manifest, + ) diff --git a/aymurai/experiments/entity_disambiguation/mlflow_logging.py b/aymurai/experiments/entity_disambiguation/mlflow_logging.py new file mode 100644 index 00000000..b9e0a799 --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/mlflow_logging.py @@ -0,0 +1,111 @@ +import json +import tempfile +from pathlib import Path + +import mlflow + +from aymurai.experiments.entity_disambiguation.config import ExperimentRunConfig +from aymurai.experiments.entity_disambiguation.manifest import DatasetInfo +from aymurai.utils.yaml_data import save_yaml + + +def configure_mlflow(config: ExperimentRunConfig) -> None: + """ + Configure MLflow tracking URI and experiment name. + + Args: + config (ExperimentRunConfig): Experiment run configuration. + """ + mlflow.set_tracking_uri(config.logging.mlflow.tracking_uri) + mlflow.set_experiment(config.logging.mlflow.experiment_name) + + +def log_run_metadata( + config: ExperimentRunConfig, + *, + run_name: str | None = None, + preds_dir_name: str | None = None, + dataset_info: DatasetInfo | None = None, + metrics: dict[str, float] | None = None, + per_doc_scores: dict[str, object] | None = None, +) -> None: + """ + Log run metadata to MLflow. + + Args: + config (ExperimentRunConfig): Experiment run configuration. + run_name (str | None, optional): Run name. Defaults to None. + preds_dir_name (str | None, optional): Predictions directory name. Defaults to None. + dataset_info (DatasetInfo | None, optional): Dataset information. Defaults to None. + metrics (dict[str, float] | None, optional): Metrics to log. Defaults to None. + per_doc_scores (dict[str, object] | None, optional): Per-document scores to log. Defaults to None. + """ + params = { + "model_provider": config.model.provider, + "model_name": config.model.name, + "system_prompt_id": config.prompts.system.id, + "user_prompt_id": config.prompts.user.id, + "preds_dir_template": config.predictions.dir_name_template, + } + if run_name: + params["run_name"] = run_name + if preds_dir_name: + params["preds_dir_name"] = preds_dir_name + if config.model.temperature is not None: + params["temperature"] = config.model.temperature + if config.model.max_tokens is not None: + params["max_tokens"] = config.model.max_tokens + if config.data.dataset_id: + params["dataset_id"] = config.data.dataset_id + + if dataset_info: + params.update( + { + "dataset_hash": dataset_info.dataset_hash, + "dataset_count": dataset_info.dataset_count, + "dataset_bytes_total": dataset_info.dataset_bytes_total, + } + ) + + mlflow.log_params(params) + + if metrics: + mlflow.log_metrics(metrics) + + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + config_payload = config.model_dump(mode="json") + if not config.logging.privacy.log_prompt_text: + config_payload["prompts"]["system"]["text"] = "" + config_payload["prompts"]["user"]["text"] = "" + config_path = tmp_path / "config.yaml" + save_yaml(config_payload, str(config_path)) + mlflow.log_artifact(str(config_path), artifact_path="config") + + if config.logging.privacy.log_prompt_text: + system_path = tmp_path / "system_prompt.txt" + user_path = tmp_path / "user_prompt.txt" + system_path.write_text(config.prompts.system.text or "", encoding="utf-8") + user_path.write_text(config.prompts.user.text or "", encoding="utf-8") + mlflow.log_artifact(str(system_path), artifact_path="prompts") + mlflow.log_artifact(str(user_path), artifact_path="prompts") + + if ( + config.logging.privacy.log_data_manifest + and dataset_info + and dataset_info.manifest + ): + manifest_path = tmp_path / "manifest.json" + manifest_path.write_text( + json.dumps(dataset_info.manifest, indent=2, sort_keys=True), + encoding="utf-8", + ) + mlflow.log_artifact(str(manifest_path), artifact_path="data") + + if config.logging.privacy.log_per_doc_scores and per_doc_scores: + scores_path = tmp_path / "per_doc_scores.json" + scores_path.write_text( + json.dumps(per_doc_scores, indent=2, sort_keys=True), + encoding="utf-8", + ) + mlflow.log_artifact(str(scores_path), artifact_path="metrics") diff --git a/aymurai/experiments/entity_disambiguation/runner.py b/aymurai/experiments/entity_disambiguation/runner.py new file mode 100644 index 00000000..2cc372ce --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/runner.py @@ -0,0 +1,468 @@ +import argparse +import json +import mimetypes +import os +import re +import time +from pathlib import Path +from typing import Any, Iterable + +import mlflow +import requests +from more_itertools import unique_everseen +from tqdm import tqdm + +from aymurai.evaluation.metrics import evaluate_disambiguation +from aymurai.experiments.entity_disambiguation.config import ( + load_experiment_config, + render_run_name, +) +from aymurai.experiments.entity_disambiguation.manifest import build_metadata_manifest +from aymurai.experiments.entity_disambiguation.mlflow_logging import ( + configure_mlflow, + log_run_metadata, +) +from aymurai.llm_providers import OllamaLLMProvider +from aymurai.logger import get_logger +from aymurai.meta.entities import CanonicalEntities, CanonicalEntity +from aymurai.utils.json_data import get_pretty, load_json, save_json +from aymurai.utils.paths import prediction_filename_for_test, test_id_from_filename + +logger = get_logger(__name__) + +DOC_EXTENSIONS = {".pdf", ".docx"} + + +def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]: + """ + Discover documents in a directory with given extensions. + + Args: + root (Path): Root directory to search for documents. + extensions (Iterable[str]): File extensions to include. + + Returns: + list[Path]: List of discovered document paths with the specified extensions. + """ + extensions = {ext.lower() for ext in extensions} + return sorted( + path + for path in root.rglob("*") + if path.is_file() and path.suffix.lower() in extensions + ) + + +def call_extraction_api( + session: requests.Session, endpoint: str, file_path: Path, timeout_s: float +) -> dict[str, object]: + """ + Call the extraction API with a document file. + + Args: + session (requests.Session): HTTP session for making requests. + endpoint (str): URL of the extraction API endpoint. + file_path (Path): Path to the document file to be processed. + timeout_s (float): Request timeout in seconds. + + Returns: + dict[str, object]: Payload containing the response details. + """ + payload: dict[str, object] = { + "path": str(file_path), + "status": "failure", + "status_code": None, + "elapsed_s": None, + "detail": None, + } + + if not file_path.exists(): + payload["detail"] = "File does not exist" + return payload + + mime_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream" + files = { + "file": (file_path.name, file_path.open("rb"), mime_type), + } + + try: + start = time.perf_counter() + response = session.post( + endpoint, + files=files, + timeout=timeout_s, + ) + elapsed = time.perf_counter() - start + except requests.RequestException as exc: + payload["detail"] = f"Request failed: {exc}" + return payload + finally: + files["file"][1].close() + + payload["status_code"] = response.status_code + payload["elapsed_s"] = elapsed + + try: + response_body = response.json() + except ValueError: + response_body = {"raw": response.text[:500]} + + if response.ok: + payload["status"] = "success" + payload["detail"] = { + "document_id": response_body.get("document_id"), + "document": response_body.get("document", []), + } + else: + payload["detail"] = response_body + + return payload + + +def get_predictions( + session: requests.Session, endpoint: str, sample: str, timeout_s: float +) -> dict: + """ + Get predictions from the prediction API for a given text sample. + + Args: + session (requests.Session): HTTP session for making requests. + endpoint (str): URL of the prediction API endpoint. + sample (str): Text sample to be predicted. + timeout_s (float): Request timeout in seconds. + + Returns: + dict: Prediction response as a dictionary. + """ + response = session.post(url=endpoint, json={"text": sample}, timeout=timeout_s) + response.raise_for_status() + return response.json() + + +def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]: + """ + Parse prediction labels to extract unique aymurai labels and their alternative texts. + + Args: + predictions (list[dict[str, Any]]): List of prediction dictionaries. + + Returns: + list[dict[str, str]]: List of dictionaries containing unique aymurai labels and their alternative texts. + """ + attrs_stream = ( + label.get("attrs") or {} + for label in (label for pred in predictions for label in pred.get("labels", ())) + ) + + unique_pairs = unique_everseen( + ( + attrs.get("aymurai_label"), + attrs.get("aymurai_alt_text"), + ) + for attrs in attrs_stream + if attrs.get("aymurai_label") and attrs.get("aymurai_alt_text") + ) + + return sorted( + ({"aymurai_label": label, "text": text} for label, text in unique_pairs), + key=lambda item: (item["aymurai_label"], item["text"]), + ) + + +def sanitize_filename(path: Path) -> str: + """ + Sanitize a filename by replacing spaces and underscores with hyphens and collapsing multiple hyphens. + + Args: + path (Path): Path object representing the file. + + Returns: + str: Sanitized filename as a string. + """ + base = os.path.splitext(path.name)[0] + target = re.sub(r"\s+|_", "-", base) + target = re.sub(r"-{2,}", "-", target) + return target + + +def extract_canonical_entities( + *, + session: requests.Session, + doc_path: Path, + extract_endpoint: str, + predict_endpoint: str, + timeout_s: float, + model_name: str, + system_prompt: str, + user_prompt_template: str, + temperature: float | None, + max_tokens: int | None, +) -> list[CanonicalEntity]: + """ + Extract canonical entities from a document using extraction and prediction APIs. + + Args: + session (requests.Session): HTTP session for making requests. + doc_path (Path): Path to the document file. + extract_endpoint (str): URL of the extraction API endpoint. + predict_endpoint (str): URL of the prediction API endpoint. + timeout_s (float): Request timeout in seconds. + model_name (str): Name of the LLM model to use. + system_prompt (str): System prompt text. + user_prompt_template (str): User prompt template text. + temperature (float | None): Temperature setting for the LLM. + max_tokens (int | None): Maximum tokens setting for the LLM. + + Raises: + ValueError: If the document text is empty or not found. + + Returns: + list[CanonicalEntity]: List of extracted canonical entities. + """ + document_payload = call_extraction_api( + session, + extract_endpoint, + doc_path, + timeout_s, + ) + document = document_payload.get("detail", {}).get("document") + + if not document: + raise ValueError("Document text is empty or not found.") + + ner_predictions = [ + get_predictions(session, predict_endpoint, paragraph, timeout_s) + for paragraph in tqdm(document, desc=f"NER {doc_path.name}") + ] + parsed_ner_labels = parse_prediction_labels(ner_predictions) + ner_output_json = get_pretty(parsed_ner_labels) + + user_prompt = user_prompt_template.format( + document_text="\n".join(document).strip(), + ner_output_json=ner_output_json, + ) + + provider = OllamaLLMProvider(model=model_name) + options: dict[str, object] = {} + if temperature is not None: + options["temperature"] = temperature + if max_tokens is not None: + options["num_predict"] = max_tokens + + generate_kwargs: dict[str, object] = { + "messages": [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt}, + ], + "format": CanonicalEntities.model_json_schema(), + } + if options: + generate_kwargs["options"] = options + + response = provider.generate(**generate_kwargs) + + payload = json.loads(response.text) + canonical_entities = [ + CanonicalEntity.model_validate(output) + for output in payload.get("canonical_entities", []) + ] + + return canonical_entities + + +def run_experiment(config_path: str) -> None: + """ + Run the entity disambiguation experiment based on the provided configuration. + + Args: + config_path (str): Path to the experiment configuration file. + + Raises: + FileNotFoundError: If the input directory is not found. + FileNotFoundError: If the ground truth directory is not found. + RuntimeError: If no documents are found in the input directory. + ValueError: If there is an error in processing the documents. + """ + config = load_experiment_config(config_path) + + input_dir = Path(config.data.input_dir) + ground_truth_dir = Path(config.data.ground_truth_dir) + + if not input_dir.exists(): + raise FileNotFoundError(f"Input directory not found: {input_dir}") + if not ground_truth_dir.exists(): + raise FileNotFoundError(f"Ground truth directory not found: {ground_truth_dir}") + + documents = discover_documents(input_dir, DOC_EXTENSIONS) + if not documents: + raise RuntimeError(f"No documents found under {input_dir}") + + timestamp = time.strftime("%y%m%d_%H%M") + run_name = render_run_name( + config.experiment.run_name, + config.model.name, + config.prompts.system.id, + config.prompts.user.id, + timestamp=timestamp, + ) + preds_dir_name = render_run_name( + config.predictions.dir_name_template, + config.model.name, + config.prompts.system.id, + config.prompts.user.id, + timestamp=timestamp, + ) + output_dir = Path(config.predictions.output_base_dir) / preds_dir_name + output_dir.mkdir(parents=True, exist_ok=False) + + dataset_info = ( + build_metadata_manifest(ground_truth_dir) + if config.data.manifest.enabled + else None + ) + + api_base_url = os.getenv("API_BASE_URL", "http://localhost:8899") + extract_endpoint = f"{api_base_url}/misc/document-extract" + predict_endpoint = f"{api_base_url}/anonymizer/predict" + timeout_s = float(os.getenv("REQUEST_TIMEOUT", "30")) + + logger.info(f"Processing {len(documents)} documents from {input_dir}") + logger.info(f"Predictions will be stored in {output_dir}") + + configure_mlflow(config) + + per_doc_scores: dict[str, object] = {} + metrics_summary: dict[str, float] = {} + + with mlflow.start_run(run_name=run_name): + session = requests.Session() + + processed = 0 + failed = 0 + + for doc_path in documents: + logger.info(f"Processing document: {doc_path.name}") + try: + canonical_entities = extract_canonical_entities( + session=session, + doc_path=doc_path, + extract_endpoint=extract_endpoint, + predict_endpoint=predict_endpoint, + timeout_s=timeout_s, + model_name=config.model.name, + system_prompt=config.prompts.system.text or "", + user_prompt_template=config.prompts.user.text or "", + temperature=config.model.temperature, + max_tokens=config.model.max_tokens, + ) + target_filename = sanitize_filename(doc_path) + target_path = output_dir / f"{target_filename}.json" + save_json( + [ + entity.model_dump() + | { + "entity_id": entity.entity_id.hex + if entity.entity_id + else None + } + for entity in canonical_entities + ], + str(target_path), + ) + processed += 1 + except Exception as exc: + failed += 1 + logger.warning(f"Error processing {doc_path.name}: {exc}") + + if config.evaluation.function != "evaluate_disambiguation": + raise ValueError( + f"Unsupported evaluation function: {config.evaluation.function}" + ) + + results = [] + if output_dir.exists(): + for test_path in sorted(ground_truth_dir.glob("*.json")): + doc_id = test_id_from_filename(test_path) + pred_path = output_dir / prediction_filename_for_test(test_path) + + if not pred_path.exists(): + logger.warning( + f"Prediction for {test_path.name} was not found; skipping." + ) + continue + + gold_data = load_json(str(test_path)) + pred_data = load_json(str(pred_path)) + + score, metrics = evaluate_disambiguation( + gold_data, + pred_data, + w_ent=config.evaluation.weights.get("w_ent", 0.4), + w_alias=config.evaluation.weights.get("w_alias", 0.35), + w_label=config.evaluation.weights.get("w_label", 0.2), + w_role=config.evaluation.weights.get("w_role", 0.05), + sim_threshold=config.evaluation.sim_threshold or 0.3, + normalize=config.evaluation.normalize, + ) + results.append((doc_id, score, metrics)) + + average = ( + sum(score for _, score, _ in results) / len(results) + if results + else float("nan") + ) + + if results: + metrics_summary["score_avg"] = average + metrics_summary["doc_count"] = float(len(results)) + + metric_names = set(results[0][2].keys()) + for metric_name in metric_names: + metrics_summary[f"{metric_name}_avg"] = sum( + metrics.get(metric_name, 0.0) for _, _, metrics in results + ) / len(results) + + for doc_id, score, metrics in results: + per_doc_scores[doc_id] = {"score": score, **metrics} + + metrics_summary["docs_processed"] = float(processed) + metrics_summary["docs_failed"] = float(failed) + + log_run_metadata( + config, + run_name=run_name, + preds_dir_name=preds_dir_name, + dataset_info=dataset_info, + metrics=metrics_summary if metrics_summary else None, + per_doc_scores=per_doc_scores if per_doc_scores else None, + ) + + +def build_arg_parser() -> argparse.ArgumentParser: + """ + Build the argument parser for the entity disambiguation experiment. + + Returns: + argparse.ArgumentParser: The configured argument parser. + """ + parser = argparse.ArgumentParser( + description="Run entity disambiguation experiment from a YAML config.", + ) + parser.add_argument( + "--config", + required=True, + help="Path to the experiment YAML config.", + ) + return parser + + +def main() -> None: + """ + Main entry point for the entity disambiguation experiment script. + Parses command-line arguments and runs the experiment. + """ + args = build_arg_parser().parse_args() + run_experiment(args.config) + + +if __name__ == "__main__": + main() diff --git a/aymurai/utils/paths.py b/aymurai/utils/paths.py new file mode 100644 index 00000000..0677c72e --- /dev/null +++ b/aymurai/utils/paths.py @@ -0,0 +1,40 @@ +from pathlib import Path + +TEST_SUFFIX = "-test" + + +def test_id_from_filename(test_path: Path, *, test_suffix: str = TEST_SUFFIX) -> str: + """ + Convert a test filename like "document-1-test.json" into "document-1". + Falls back to stripping ".json" when the suffix is not present. + + Args: + test_path (Path): Path to the test file. + test_suffix (str, optional): Suffix indicating test files. Defaults to "-test". + + Returns: + str: The test identifier extracted from the filename. + """ + name = Path(test_path).name + suffix_with_ext = f"{test_suffix}.json" + if name.endswith(suffix_with_ext): + return name[: -len(suffix_with_ext)] + if name.endswith(".json"): + return name[: -len(".json")] + return name + + +def prediction_filename_for_test( + test_path: Path, *, test_suffix: str = TEST_SUFFIX +) -> str: + """ + Return the prediction filename that should correspond to the test file. + + Args: + test_path (Path): Path to the test file. + test_suffix (str, optional): Suffix indicating test files. Defaults to "-test". + + Returns: + str: The prediction filename corresponding to the test file. + """ + return f"{test_id_from_filename(test_path, test_suffix=test_suffix)}.json" diff --git a/docker-compose.yml b/docker-compose.yml index 3876bb91..eb8d8fe0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,9 +34,87 @@ x-ollama: &ollama-base shm_size: "2gb" services: + mlflow: + image: ghcr.io/mlflow/mlflow:latest + container_name: mlflow + ports: + - "5000:5000" + environment: + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + MLFLOW_S3_ENDPOINT_URL: http://minio:9000 + MLFLOW_S3_IGNORE_TLS: ${MLFLOW_S3_IGNORE_TLS} + command: + [ + "/bin/sh", + "-c", + "pip install --no-cache-dir psycopg2-binary boto3 && mlflow server --host 0.0.0.0 --port 5000 --backend-store-uri postgresql://mlflow:mlflow@postgres:5432/mlflow --default-artifact-root ${MLFLOW_ARTIFACT_ROOT}", + ] + depends_on: + postgres: + condition: service_healthy + minio: + condition: service_started + healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request; urllib.request.urlopen('http://localhost:5000')", + ] + interval: 10s + timeout: 5s + retries: 6 + + postgres: + image: postgres:16 + container_name: postgres + ports: + - "5432:5432" + environment: + POSTGRES_USER: mlflow + POSTGRES_PASSWORD: mlflow + POSTGRES_DB: mlflow + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U mlflow"] + interval: 10s + timeout: 5s + retries: 6 + + minio: + image: minio/minio:latest + container_name: minio + ports: + - "9002:9000" + - "9001:9001" + environment: + MINIO_ROOT_USER: ${AWS_ACCESS_KEY_ID} + MINIO_ROOT_PASSWORD: ${AWS_SECRET_ACCESS_KEY} + command: ["server", "/data", "--console-address", ":9001"] + volumes: + - minio-data:/data + # Healthcheck removed; minio image lacks wget and minio-mc already waits for readiness. + + minio-mc: + image: minio/mc:latest + container_name: minio-mc + depends_on: + minio: + condition: service_started + entrypoint: + [ + "/bin/sh", + "-c", + "until (/usr/bin/mc alias set local http://minio:9000 ${AWS_ACCESS_KEY_ID} ${AWS_SECRET_ACCESS_KEY}) do sleep 1; done; /usr/bin/mc mb -p local/mlflow; /usr/bin/mc anonymous set none local/mlflow; exit 0;", + ] + # CPU-friendly API aymurai-api: <<: *api-lite + container_name: aymurai-api depends_on: - ollama environment: @@ -46,6 +124,7 @@ services: # GPU-enabled API aymurai-api-gpu: <<: *api-lite + container_name: aymurai-api-gpu depends_on: - ollama-gpu environment: @@ -62,6 +141,7 @@ services: # CPU-friendly Full API aymurai-api-full: <<: *api-full + container_name: aymurai-api-full depends_on: - ollama environment: @@ -76,6 +156,7 @@ services: # GPU-enabled Full API aymurai-api-full-gpu: <<: *api-full + container_name: aymurai-api-full-gpu depends_on: - ollama-gpu environment: @@ -95,10 +176,12 @@ services: # CPU-only Ollama (default) ollama: <<: *ollama-base + container_name: ollama # GPU-enabled Ollama (run instead on GPU hosts) ollama-gpu: <<: *ollama-base + container_name: ollama-gpu deploy: resources: reservations: @@ -109,3 +192,5 @@ services: volumes: ollama: + postgres-data: + minio-data: diff --git a/docs/experiments/README.md b/docs/experiments/README.md new file mode 100644 index 00000000..87c1f771 --- /dev/null +++ b/docs/experiments/README.md @@ -0,0 +1,90 @@ +# Experiments Setup + +This guide walks through running the entity-disambiguation experiments with MLflow +tracking and MinIO artifacts. + +## 1) Update the devcontainer + +The devcontainer installs base dependencies via `uv`. The MLflow client lives in the +`mlops` dependency group, so install it after rebuilding the container: + +```bash +uv sync --frozen --all-extras --group mlops +``` + +If you use VS Code, run "Dev Containers: Rebuild and Reopen in Container" first. + +## 2) Configure environment + +- Set MinIO credentials in `.env`: + - `AWS_ACCESS_KEY_ID` + - `AWS_SECRET_ACCESS_KEY` +- Verify MLflow settings in `.env.common`: + - `MLFLOW_TRACKING_URI` + - `MLFLOW_S3_ENDPOINT_URL` + - `MLFLOW_ARTIFACT_ROOT` + +The experiment runner also uses: +- `API_BASE_URL` (defaults to `http://localhost:8899`) +- `REQUEST_TIMEOUT` (defaults to `300`) + +## 3) Start services + +```bash +make mlops-up +make ollama-up +make api-up +``` + +If you need the GPU services, set: +```bash +make ollama-up OLLAMA_SERVICE=ollama-gpu +make api-up API_SERVICE=aymurai-api-gpu +``` + +## 4) Pull the model + +```bash +make ollama-pull MODEL=gemma3:270m +``` + +## 5) Run the experiment + +```bash +make exp-run CONFIG=resources/experiments/entity-disambiguation/exp-gemma3-pv1.yaml +``` + +## Predictions vs. ground truth filenames + +Ground-truth files are expected to end with `-test.json`, while predictions use +the same base name without the suffix. + +Example: +- `document-1-test.json` (ground truth) +- `document-1.json` (prediction) + +Predictions are written to disk (not logged as artifacts). MLflow logs: +- Run params (model, prompt IDs, dataset hash, etc.) +- Aggregate metrics and per-document scores (as an artifact) +- Prompt text (if enabled in config) + +## 6) Access MLflow UI + +If running remotely, forward the MLflow port: +```bash +ssh -L 5000:127.0.0.1:5000 user@remote-host +``` + +Then open `http://localhost:5000`. + +## Config location + +Experiment configs live under: +- `resources/experiments/entity-disambiguation/` + +Use the template at `docs/experiments/base.yaml` when creating new experiment +configs. Copy it into `resources/experiments/entity-disambiguation/` and adjust +the model, prompts, and data paths as needed. + +The main runner is: +- `aymurai/experiments/entity_disambiguation/runner.py` diff --git a/docs/experiments/base.yaml b/docs/experiments/base.yaml new file mode 100644 index 00000000..a3fb6adc --- /dev/null +++ b/docs/experiments/base.yaml @@ -0,0 +1,50 @@ +experiment: + name: "entity-disambiguation" + run_name: "{model}-{system_id}-{user_id}-{timestamp}" + +model: + provider: "ollama" + name: "llama3:8b" + temperature: 0.2 + max_tokens: 512 + +prompts: + system: + id: "sys-001" + text: "" + user: + id: "user-001" + text: "" + + +data: + ground_truth_dir: "/data/gt" + input_dir: "/data/input" + dataset_id: "gt-v1" + manifest: + enabled: true + mode: "metadata_only" + +predictions: + output_base_dir: "/data/preds" + dir_name_template: "preds-{model}-{system_id}-{user_id}-{timestamp}" + +logging: + mlflow: + experiment_name: "entity-disambiguation" + privacy: + log_prompt_text: true + log_data_manifest: false + log_per_doc_scores: true + per_doc_scores: + id_strategy: "anon_filename" + +evaluation: + function: "evaluate_disambiguation" + weights: + w_ent: 0.4 + w_alias: 0.35 + w_label: 0.2 + w_role: 0.05 + per_doc_scores: true + normalize: true diff --git a/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md b/docs/metrics/Desarrollo metrica.md similarity index 98% rename from notebooks/experiments/entity-disambiguation/Desarrollo metrica.md rename to docs/metrics/Desarrollo metrica.md index 5f02a5f2..13fab9b7 100644 --- a/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md +++ b/docs/metrics/Desarrollo metrica.md @@ -349,8 +349,8 @@ def compute_metrics_components( gold_labels = [e.get("aymurai_label") for e in gold_entities] pred_labels = [e.get("aymurai_label") for e in pred_entities] - gold_roles = [e.get("attributes", {}).get("rol") for e in gold_entities] - pred_roles = [e.get("attributes", {}).get("rol") for e in pred_entities] + gold_roles = [e.get("attributes", {}).get("role") for e in gold_entities] + pred_roles = [e.get("attributes", {}).get("role") for e in pred_entities] sim_matrix = [ [alias_jaccard(g_gold, g_pred) for g_pred in pred_aliases] diff --git a/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb index 65318651..d8ff91eb 100644 --- a/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb +++ b/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb @@ -53,13 +53,13 @@ "metadata": {}, "outputs": [], "source": [ - "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", - "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", + "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8899\")\n", + "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", "DATA_ROOT = Path(\n", " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/disambiguation-eval\")\n", ")\n", "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", "\n", "print(f\"Target endpoint: {ENDPOINT}\")\n", "print(f\"Data root: {DATA_ROOT.resolve()}\")" @@ -123,7 +123,7 @@ " response = session.post(\n", " ENDPOINT,\n", " files=files,\n", - " timeout=REQUEST_TIMEOUT_S,\n", + " timeout=REQUEST_TIMEOUT,\n", " )\n", " elapsed = time.perf_counter() - start\n", " except requests.RequestException as exc:\n", @@ -169,7 +169,9 @@ "source": [ "# Function to make inference using the API\n", "def get_predictions(sample: str) -> dict:\n", - " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample})\n", + " response = requests.post(\n", + " url=f\"{API_BASE_URL}/anonymizer/predict\", json={\"text\": sample}\n", + " )\n", " response.raise_for_status()\n", " return response.json()" ] @@ -236,9 +238,9 @@ "# Available models\n", "# model = \"gpt-oss:20b\"\n", "# model = \"llama3\"\n", - "# model = \"phi4:14b\"\n", + "model = \"phi4:14b\"\n", "# model = \"llama3.1:8b\"\n", - "model = \"gemma3:270m\"" + "# model = \"gemma3:270m\"" ] }, { @@ -482,7 +484,7 @@ ], "metadata": { "kernelspec": { - "display_name": "aymurai", + "display_name": "aymurai (3.10.19)", "language": "python", "name": "python3" }, @@ -496,7 +498,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.18" + "version": "3.10.19" } }, "nbformat": 4, diff --git a/notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb b/notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb index 05c54a17..0797844a 100644 --- a/notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb +++ b/notebooks/experiments/entity-disambiguation/03-disambiguation-evaluation.ipynb @@ -27,8 +27,8 @@ "# Ajusta estas rutas antes de ejecutar\n", "TEST_DIR = Path(\"/resources/data/restricted/disambiguation-eval/test\")\n", "PREDS_DIR = Path(\n", - " \"/resources/data/restricted/disambiguation-eval/preds/preds_phi4:14b_pv1_251125_1716\"\n", - ")" + " \"/resources/data/restricted/disambiguation-eval/preds/preds-phi4:14b-sys-001-user-001-251228_0159\"\n", + ")\n" ] }, { @@ -88,7 +88,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "aymurai (3.10.19)", "language": "python", "name": "python3" }, diff --git a/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb b/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb index e0f49b96..e907c3fc 100644 --- a/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb +++ b/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb @@ -51,13 +51,13 @@ "metadata": {}, "outputs": [], "source": [ - "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8999\")\n", - "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", + "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8999\")\n", + "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", "DATA_ROOT = Path(\n", " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/disambiguation-eval\")\n", ")\n", "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", "\n", "print(f\"Target endpoint: {ENDPOINT}\")\n", "print(f\"Data root: {DATA_ROOT.resolve()}\")" @@ -104,7 +104,7 @@ " response = session.post(\n", " ENDPOINT,\n", " files=files,\n", - " timeout=REQUEST_TIMEOUT_S,\n", + " timeout=REQUEST_TIMEOUT,\n", " )\n", " elapsed = time.perf_counter() - start\n", " except requests.RequestException as exc:\n", diff --git a/notebooks/experiments/summarization/01-summarization-benchmark.ipynb b/notebooks/experiments/summarization/01-summarization-benchmark.ipynb index 1178ac62..e2a6558a 100644 --- a/notebooks/experiments/summarization/01-summarization-benchmark.ipynb +++ b/notebooks/experiments/summarization/01-summarization-benchmark.ipynb @@ -63,13 +63,13 @@ "metadata": {}, "outputs": [], "source": [ - "BASE_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", - "ENDPOINT = f\"{BASE_URL}/misc/document-extract\"\n", + "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8899\")\n", + "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", "DATA_ROOT = Path(\n", " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", ")\n", "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", "\n", "print(f\"Target endpoint: {ENDPOINT}\")\n", "print(f\"Data root: {DATA_ROOT.resolve()}\")" @@ -133,7 +133,7 @@ " response = session.post(\n", " ENDPOINT,\n", " files=files,\n", - " timeout=REQUEST_TIMEOUT_S,\n", + " timeout=REQUEST_TIMEOUT,\n", " )\n", " elapsed = time.perf_counter() - start\n", " except requests.RequestException as exc:\n", diff --git a/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb b/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb index b40fb1a2..f54a1005 100644 --- a/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb +++ b/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb @@ -105,13 +105,13 @@ "metadata": {}, "outputs": [], "source": [ - "BASE_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://localhost:8899\")\n", - "ENDPOINT = f\"{BASE_URL}/misc/document-extract\"\n", + "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8899\")\n", + "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", "DATA_ROOT = Path(\n", " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", ")\n", "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", "\n", "print(f\"Target endpoint: {ENDPOINT}\")\n", "print(f\"Data root: {DATA_ROOT.resolve()}\")" @@ -175,7 +175,7 @@ " response = session.post(\n", " ENDPOINT,\n", " files=files,\n", - " timeout=REQUEST_TIMEOUT_S,\n", + " timeout=REQUEST_TIMEOUT,\n", " )\n", " elapsed = time.perf_counter() - start\n", " except requests.RequestException as exc:\n", diff --git a/pyproject.toml b/pyproject.toml index 94e817e0..51e6aa24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,13 +96,11 @@ Homepage = "https://www.aymurai.info/" Repository = "https://github.com/aymurAI/backend" Issues = "https://github.com/AymurAI/backend/issues" -# [project.optional-dependencies] -# gpu = [ -# "torch @ https://download.pytorch.org/whl/cu113/torch-1.12.1%2Bcu113-cp310-cp310-linux_x86_64.whl", -# "spacy[cuda113]==3.8.3", -# ] - [dependency-groups] +mlops = [ + "mlflow>=2.12.1", + "boto3>=1.34.0", +] dev = [ "matplotlib>=3.10.0", "seaborn>=0.13.2", diff --git a/uv.lock b/uv.lock index 78db5ef7..dabb9fdc 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ wheels = [ [[package]] name = "accelerate" -version = "1.2.1" +version = "1.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -28,9 +28,9 @@ dependencies = [ { name = "safetensors" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/09/7947691b7d44bfc739da4a44cc47d6a6d75e6fe9adf047c5234d7cb6be64/accelerate-1.2.1.tar.gz", hash = "sha256:03e161fc69d495daf2b9b5c8d5b43d06e2145520c04727b5bda56d49f1a43ab5", size = 341652 } +sdist = { url = "https://files.pythonhosted.org/packages/4a/8e/ac2a9566747a93f8be36ee08532eb0160558b07630a081a6056a9f89bf1d/accelerate-1.12.0.tar.gz", hash = "sha256:70988c352feb481887077d2ab845125024b2a137a5090d6d7a32b57d03a45df6", size = 398399 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/60/a585c806d6c0ec5f8149d44eb202714792802f484e6e2b1bf96b23bd2b00/accelerate-1.2.1-py3-none-any.whl", hash = "sha256:be1cbb958cf837e7cdfbde46b812964b1b8ae94c9c7d94d921540beafcee8ddf", size = 336355 }, + { url = "https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl", hash = "sha256:3e2091cd341423207e2f084a6654b1efcd250dc326f2a37d6dde446e07cabb11", size = 380935 }, ] [[package]] @@ -44,7 +44,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.2" +version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -56,25 +56,25 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994 } +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/34/939730e66b716b76046dedfe0842995842fa906ccc4964bba414ff69e429/aiohttp-3.13.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2372b15a5f62ed37789a6b383ff7344fc5b9f243999b0cd9b629d8bc5f5b4155", size = 736471 }, - { url = "https://files.pythonhosted.org/packages/fd/cf/dcbdf2df7f6ca72b0bb4c0b4509701f2d8942cf54e29ca197389c214c07f/aiohttp-3.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7f8659a48995edee7229522984bd1009c1213929c769c2daa80b40fe49a180c", size = 493985 }, - { url = "https://files.pythonhosted.org/packages/9d/87/71c8867e0a1d0882dcbc94af767784c3cb381c1c4db0943ab4aae4fed65e/aiohttp-3.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:939ced4a7add92296b0ad38892ce62b98c619288a081170695c6babe4f50e636", size = 489274 }, - { url = "https://files.pythonhosted.org/packages/38/0f/46c24e8dae237295eaadd113edd56dee96ef6462adf19b88592d44891dc5/aiohttp-3.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6315fb6977f1d0dd41a107c527fee2ed5ab0550b7d885bc15fee20ccb17891da", size = 1668171 }, - { url = "https://files.pythonhosted.org/packages/eb/c6/4cdfb4440d0e28483681a48f69841fa5e39366347d66ef808cbdadddb20e/aiohttp-3.13.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e7352512f763f760baaed2637055c49134fd1d35b37c2dedfac35bfe5cf8725", size = 1636036 }, - { url = "https://files.pythonhosted.org/packages/84/37/8708cf678628216fb678ab327a4e1711c576d6673998f4f43e86e9ae90dd/aiohttp-3.13.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e09a0a06348a2dd73e7213353c90d709502d9786219f69b731f6caa0efeb46f5", size = 1727975 }, - { url = "https://files.pythonhosted.org/packages/e6/2e/3ebfe12fdcb9b5f66e8a0a42dffcd7636844c8a018f261efb2419f68220b/aiohttp-3.13.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a09a6d073fb5789456545bdee2474d14395792faa0527887f2f4ec1a486a59d3", size = 1815823 }, - { url = "https://files.pythonhosted.org/packages/a1/4f/ca2ef819488cbb41844c6cf92ca6dd15b9441e6207c58e5ae0e0fc8d70ad/aiohttp-3.13.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b59d13c443f8e049d9e94099c7e412e34610f1f49be0f230ec656a10692a5802", size = 1669374 }, - { url = "https://files.pythonhosted.org/packages/f8/fe/1fe2e1179a0d91ce09c99069684aab619bf2ccde9b20bd6ca44f8837203e/aiohttp-3.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20db2d67985d71ca033443a1ba2001c4b5693fe09b0e29f6d9358a99d4d62a8a", size = 1555315 }, - { url = "https://files.pythonhosted.org/packages/5a/2b/f3781899b81c45d7cbc7140cddb8a3481c195e7cbff8e36374759d2ab5a5/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:960c2fc686ba27b535f9fd2b52d87ecd7e4fd1cf877f6a5cba8afb5b4a8bd204", size = 1639140 }, - { url = "https://files.pythonhosted.org/packages/72/27/c37e85cd3ece6f6c772e549bd5a253d0c122557b25855fb274224811e4f2/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6c00dbcf5f0d88796151e264a8eab23de2997c9303dd7c0bf622e23b24d3ce22", size = 1645496 }, - { url = "https://files.pythonhosted.org/packages/66/20/3af1ab663151bd3780b123e907761cdb86ec2c4e44b2d9b195ebc91fbe37/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fed38a5edb7945f4d1bcabe2fcd05db4f6ec7e0e82560088b754f7e08d93772d", size = 1697625 }, - { url = "https://files.pythonhosted.org/packages/95/eb/ae5cab15efa365e13d56b31b0d085a62600298bf398a7986f8388f73b598/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b395bbca716c38bef3c764f187860e88c724b342c26275bc03e906142fc5964f", size = 1542025 }, - { url = "https://files.pythonhosted.org/packages/e9/2d/1683e8d67ec72d911397fe4e575688d2a9b8f6a6e03c8fdc9f3fd3d4c03f/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:204ffff2426c25dfda401ba08da85f9c59525cdc42bda26660463dd1cbcfec6f", size = 1714918 }, - { url = "https://files.pythonhosted.org/packages/99/a2/ffe8e0e1c57c5e542d47ffa1fcf95ef2b3ea573bf7c4d2ee877252431efc/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05c4dd3c48fb5f15db31f57eb35374cb0c09afdde532e7fb70a75aede0ed30f6", size = 1656113 }, - { url = "https://files.pythonhosted.org/packages/0d/42/d511aff5c3a2b06c09d7d214f508a4ad8ac7799817f7c3d23e7336b5e896/aiohttp-3.13.2-cp310-cp310-win32.whl", hash = "sha256:e574a7d61cf10351d734bcddabbe15ede0eaa8a02070d85446875dc11189a251", size = 432290 }, - { url = "https://files.pythonhosted.org/packages/8b/ea/1c2eb7098b5bad4532994f2b7a8228d27674035c9b3234fe02c37469ef14/aiohttp-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:364f55663085d658b8462a1c3f17b2b84a5c2e1ba858e1b79bff7b2e24ad1514", size = 455075 }, + { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950 }, + { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099 }, + { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072 }, + { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588 }, + { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334 }, + { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656 }, + { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625 }, + { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604 }, + { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370 }, + { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023 }, + { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680 }, + { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407 }, + { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047 }, + { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264 }, + { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275 }, + { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053 }, + { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687 }, ] [[package]] @@ -107,19 +107,18 @@ wheels = [ [[package]] name = "altair" -version = "4.2.2" +version = "6.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "entrypoints" }, { name = "jinja2" }, { name = "jsonschema" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "toolz" }, + { name = "narwhals" }, + { name = "packaging" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/bf/781b607da4c1a2a7211cd570bd7e22e0accd4deaf1074c32ac7344a09339/altair-4.2.2.tar.gz", hash = "sha256:39399a267c49b30d102c10411e67ab26374156a84b1aeb9fcd15140429ba49c5", size = 740430 } +sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834 } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/62/47452306e84d4d2e67f9c559380aeb230f5e6ca84fafb428dd36b96a99ba/altair-4.2.2-py3-none-any.whl", hash = "sha256:8b45ebeaf8557f2d760c5c77b79f02ae12aee7c46c27c06014febab6f849bc87", size = 813630 }, + { url = "https://files.pythonhosted.org/packages/db/33/ef2f2409450ef6daa61459d5de5c08128e7d3edb773fefd0a324d1310238/altair-6.0.0-py3-none-any.whl", hash = "sha256:09ae95b53d5fe5b16987dccc785a7af8588f2dca50de1e7a156efa8a461515f8", size = 795410 }, ] [[package]] @@ -160,16 +159,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.12.0" +version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup" }, { name = "idna" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266 } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362 }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 }, ] [[package]] @@ -285,7 +284,7 @@ wheels = [ [[package]] name = "aymurai" -version = "2.0.0a4.dev1" +version = "2.0.0a5.dev8" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -342,6 +341,10 @@ dev = [ { name = "seaborn" }, { name = "streamlit" }, ] +mlops = [ + { name = "boto3" }, + { name = "mlflow" }, +] [package.metadata] requires-dist = [ @@ -399,6 +402,10 @@ dev = [ { name = "seaborn", specifier = ">=0.13.2" }, { name = "streamlit", specifier = ">=1.21.0" }, ] +mlops = [ + { name = "boto3", specifier = ">=1.34.0" }, + { name = "mlflow", specifier = ">=2.12.1" }, +] [[package]] name = "babel" @@ -409,15 +416,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, -] - [[package]] name = "beautifulsoup4" version = "4.14.3" @@ -475,48 +473,48 @@ wheels = [ [[package]] name = "boto3" -version = "1.42.3" +version = "1.42.23" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4e/b1add2d248d7120ee59df1ca1781a0d5fabdb9e45a477ef50c7598908e80/boto3-1.42.3.tar.gz", hash = "sha256:f7fe002c39806d5efe9105f0d74e836f001230e9520b4502c752dd4951f60041", size = 112797 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/df/17828670134e56ffca8cf8b017477f16d1a9df7ecfc3870d02aa6d4d2e20/boto3-1.42.23.tar.gz", hash = "sha256:f681a8d43b46b3d8acf0be4f3894eb85e40e75945431d0dfe0542edda7025512", size = 112845 } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/ac/81fb1f37bef7f29f7d0d4b71eb64c6425917cb230da437b42a2a58335ddf/boto3-1.42.3-py3-none-any.whl", hash = "sha256:163df55a774402fa940e95a152c308a33fb0f49cbcb5ec1725936e48635512f7", size = 140619 }, + { url = "https://files.pythonhosted.org/packages/98/8b/8e028dbf6c4bf36defe899882a00c11a8d22fcb7348e88058900c13b72a0/boto3-1.42.23-py3-none-any.whl", hash = "sha256:2ed797bdb394b08550f6269babf0a31bbeb853684bb2cb67116620df0ed632dc", size = 140574 }, ] [[package]] name = "botocore" -version = "1.42.3" +version = "1.42.23" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/e1/8238fbbab5c45def1b4f75d486a95d1d838e8e21a348563d075a2b09f74b/botocore-1.42.3.tar.gz", hash = "sha256:6bad2e512ab85926bbfb391a9486bb3120f4be71419e2f70b556d99783dcb1ce", size = 14842884 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/2c/db33716f86b67c514f895c60694a25cd7428d2137b574b59d09d626b0e2e/botocore-1.42.23.tar.gz", hash = "sha256:453ce449bd1021acd67e75c814aae1b132b1ab3ee0ecff248de863bf19e58be8", size = 14878387 } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/7f/69721ba9d89b4053ac243016428cdd2ea1a1f1c7217572a965aab9f57a20/botocore-1.42.3-py3-none-any.whl", hash = "sha256:fa174f53224ab2adc3a01bb3215b41d314df2f6578381a4a0051bd60d5c718d9", size = 14517527 }, + { url = "https://files.pythonhosted.org/packages/8d/e6/114d66f81fc4b77a8c1e45e6fcf0021018d1f4eefca0905b3da8023c9a47/botocore-1.42.23-py3-none-any.whl", hash = "sha256:d5042e0252b81f25ca1152fff9ed25463bab2438fbc4530ba53d5390d00ca1b1", size = 14551561 }, ] [[package]] name = "cachetools" -version = "6.2.2" +version = "6.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/44/ca1675be2a83aeee1886ab745b28cda92093066590233cc501890eb8417a/cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6", size = 31571 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503 }, + { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551 }, ] [[package]] name = "certifi" -version = "2025.11.12" +version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268 } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438 }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900 }, ] [[package]] @@ -660,6 +658,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599 }, ] +[[package]] +name = "cryptography" +version = "46.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004 }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667 }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807 }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615 }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800 }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707 }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541 }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464 }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838 }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596 }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782 }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381 }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988 }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451 }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007 }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248 }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089 }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029 }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222 }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280 }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958 }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714 }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970 }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236 }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642 }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126 }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573 }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695 }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720 }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740 }, + { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163 }, + { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474 }, +] + [[package]] name = "cycler" version = "0.12.1" @@ -669,9 +711,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, ] +[[package]] +name = "databricks-sdk" +version = "0.77.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/54/f2393af72974d8e70bfd44b52f17b917190b4934bab66508f4afec9e9285/databricks_sdk-0.77.0.tar.gz", hash = "sha256:56b6017ae17dc31ee821e49746d2ff627a029127166c702088428527813422bd", size = 827462 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/aa/d9186684dbcd338cf51e93621bcf53efa843fe647c0f29f549ebddbf2870/databricks_sdk-0.77.0-py3-none-any.whl", hash = "sha256:42e3211b7dbd53b81a795981149ee08d5b025442a73e464264569edc8c46ee0a", size = 779180 }, +] + [[package]] name = "datasets" -version = "4.4.1" +version = "4.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -689,9 +745,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/bf/0dae295d6d1ba0b1a200a9dd216838464b5bbd05da01407cb1330b377445/datasets-4.4.1.tar.gz", hash = "sha256:80322699aa8c0bbbdb7caa87906da689c3c2e29523cff698775c67f28fdab1fc", size = 585341 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/54/9359803da96bc65439a28fbb014dc2c90b7d4d8034a93b72362b0d40191f/datasets-4.4.2.tar.gz", hash = "sha256:9de16e415c4ba4713eac0493f7c7dc74f3aa21599297f00cc6ddab409cb7b24b", size = 586474 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5e/6f8d874366788ad5d549e9ba258037d974dda6e004843be1bda794571701/datasets-4.4.1-py3-none-any.whl", hash = "sha256:c1163de5211e42546079ab355cc0250c7e6db16eb209ac5ac6252f801f596c44", size = 511591 }, + { url = "https://files.pythonhosted.org/packages/7b/b5/fefa518c809de7bced5cddb7c21c010da66fa2ae494bda96844a280cc6ce/datasets-4.4.2-py3-none-any.whl", hash = "sha256:6f5ef3417504d9cd663c71c1b90b9a494ff4c2076a2cd6a6e40ceee6ad95befc", size = 512268 }, ] [[package]] @@ -701,15 +757,15 @@ source = { git = "https://github.com/jedzill4/datetime_matcher#0e5793e8d1e3653f7 [[package]] name = "debugpy" -version = "1.8.17" +version = "1.8.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/ad/71e708ff4ca377c4230530d6a7aa7992592648c122a2cd2b321cf8b35a76/debugpy-1.8.17.tar.gz", hash = "sha256:fd723b47a8c08892b1a16b2c6239a8b96637c62a59b94bb5dab4bac592a58a8e", size = 1644129 } +sdist = { url = "https://files.pythonhosted.org/packages/73/75/9e12d4d42349b817cd545b89247696c67917aab907012ae5b64bbfea3199/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb", size = 1644590 } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/36/b57c6e818d909f6e59c0182252921cf435e0951126a97e11de37e72ab5e1/debugpy-1.8.17-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:c41d2ce8bbaddcc0009cc73f65318eedfa3dbc88a8298081deb05389f1ab5542", size = 2098021 }, - { url = "https://files.pythonhosted.org/packages/be/01/0363c7efdd1e9febd090bb13cee4fb1057215b157b2979a4ca5ccb678217/debugpy-1.8.17-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:1440fd514e1b815edd5861ca394786f90eb24960eb26d6f7200994333b1d79e3", size = 3087399 }, - { url = "https://files.pythonhosted.org/packages/79/bc/4a984729674aa9a84856650438b9665f9a1d5a748804ac6f37932ce0d4aa/debugpy-1.8.17-cp310-cp310-win32.whl", hash = "sha256:3a32c0af575749083d7492dc79f6ab69f21b2d2ad4cd977a958a07d5865316e4", size = 5230292 }, - { url = "https://files.pythonhosted.org/packages/5d/19/2b9b3092d0cf81a5aa10c86271999453030af354d1a5a7d6e34c574515d7/debugpy-1.8.17-cp310-cp310-win_amd64.whl", hash = "sha256:a3aad0537cf4d9c1996434be68c6c9a6d233ac6f76c2a482c7803295b4e4f99a", size = 5261885 }, - { url = "https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl", hash = "sha256:60c7dca6571efe660ccb7a9508d73ca14b8796c4ed484c2002abba714226cfef", size = 5283210 }, + { url = "https://files.pythonhosted.org/packages/bf/98/d57054371887f37d3c959a7a8dc3c76b763acb65f5e78d849d7db7cadc5b/debugpy-1.8.19-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fce6da15d73be5935b4438435c53adb512326a3e11e4f90793ea87cd9f018254", size = 2098493 }, + { url = "https://files.pythonhosted.org/packages/ee/dd/c517b9aa3500157a30e4f4c4f5149f880026bd039d2b940acd2383a85d8e/debugpy-1.8.19-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:e24b1652a1df1ab04d81e7ead446a91c226de704ff5dde6bd0a0dbaab07aa3f2", size = 3087875 }, + { url = "https://files.pythonhosted.org/packages/d8/57/3d5a5b0da9b63445253107ead151eff29190c6ad7440c68d1a59d56613aa/debugpy-1.8.19-cp310-cp310-win32.whl", hash = "sha256:327cb28c3ad9e17bc925efc7f7018195fd4787c2fe4b7af1eec11f1d19bdec62", size = 5239378 }, + { url = "https://files.pythonhosted.org/packages/a6/36/7f9053c4c549160c87ae7e43800138f2695578c8b65947114c97250983b6/debugpy-1.8.19-cp310-cp310-win_amd64.whl", hash = "sha256:b7dd275cf2c99e53adb9654f5ae015f70415bbe2bacbe24cfee30d54b6aa03c5", size = 5271129 }, + { url = "https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38", size = 5292321 }, ] [[package]] @@ -787,6 +843,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, ] +[[package]] +name = "docker" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774 }, +] + [[package]] name = "docopt" version = "0.6.2" @@ -804,36 +874,31 @@ wheels = [ [[package]] name = "dspy" -version = "3.0.4" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "asyncer" }, - { name = "backoff" }, { name = "cachetools" }, { name = "cloudpickle" }, { name = "diskcache" }, { name = "gepa" }, - { name = "joblib" }, { name = "json-repair" }, { name = "litellm" }, - { name = "magicattr" }, { name = "numpy" }, { name = "openai" }, { name = "optuna" }, { name = "orjson" }, - { name = "pillow" }, { name = "pydantic" }, { name = "regex" }, { name = "requests" }, - { name = "rich" }, { name = "tenacity" }, { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/18/0042d299cd5e85fdb381568f0cfcc7769122e8f70ea0a2d33e12fd63e705/dspy-3.0.4.tar.gz", hash = "sha256:cb4529df9a91353a16144d9d94ba6ff25f36fc5adfd921f127f4c49d0e309fb8", size = 236376 } +sdist = { url = "https://files.pythonhosted.org/packages/d9/bd/339e1d949aa24e50b6edca8574cf8afbff005a1dfadc2d5b82a0003fa7da/dspy-3.1.0.tar.gz", hash = "sha256:3c1660cb687411f064509cd7ac47ed0231761f50ed92823cbbb59b3af2885702", size = 242611 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/52/56eed4828175f48f712a50a994293065afa7cc98cb112992a0b071179b6c/dspy-3.0.4-py3-none-any.whl", hash = "sha256:c0a88c7936f41f6f613ee6ca8cd92e63746ff2bd780e3896615ade7628eb6a6a", size = 285224 }, + { url = "https://files.pythonhosted.org/packages/ca/34/64e900657cad3c4df8a417fccbc4370ca851864edbd0f468210f1b57d084/dspy-3.1.0-py3-none-any.whl", hash = "sha256:b9f4d42cad9ac32b13fdf085dd3246c8ee8339c759cb291c5e7b7f9f5fb5dd72", size = 291317 }, ] [[package]] @@ -858,15 +923,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, ] -[[package]] -name = "entrypoints" -version = "0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/8d/a7121ffe5f402dc015277d2d31eb82d2187334503a011c18f2e78ecbb9b2/entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", size = 13974 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294 }, -] - [[package]] name = "exceptiongroup" version = "1.3.1" @@ -902,7 +958,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.123.9" +version = "0.128.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -910,9 +966,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/01/c3fb48c0135d89586a03c3e2c5bc04540dda52079a1af5cac4a63598efb9/fastapi-0.123.9.tar.gz", hash = "sha256:ab33d672d8e1cc6e0b49777eb73c32ccf20761011f5ca16755889ab406fd1de0", size = 355616 } +sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682 } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/15/a785e992a27620e022d0bc61b6c897ec14cff07c5ab7ff9f27651a21570b/fastapi-0.123.9-py3-none-any.whl", hash = "sha256:f54c69f23db14bd3dbcdfaf3fdce0483ca5f499512380c8e379a70cda30aa920", size = 111776 }, + { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094 }, ] [package.optional-dependencies] @@ -921,13 +977,15 @@ standard = [ { name = "fastapi-cli", extra = ["standard"] }, { name = "httpx" }, { name = "jinja2" }, + { name = "pydantic-extra-types" }, + { name = "pydantic-settings" }, { name = "python-multipart" }, { name = "uvicorn", extra = ["standard"] }, ] [[package]] name = "fastapi-cli" -version = "0.0.16" +version = "0.0.20" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, @@ -935,9 +993,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/75/9407a6b452be4c988feacec9c9d2f58d8f315162a6c7258d5a649d933ebe/fastapi_cli-0.0.16.tar.gz", hash = "sha256:e8a2a1ecf7a4e062e3b2eec63ae34387d1e142d4849181d936b23c4bdfe29073", size = 19447 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/ca/d90fb3bfbcbd6e56c77afd9d114dd6ce8955d8bb90094399d1c70e659e40/fastapi_cli-0.0.20.tar.gz", hash = "sha256:d17c2634f7b96b6b560bc16b0035ed047d523c912011395f49f00a421692bc3a", size = 19786 } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/43/678528c19318394320ee43757648d5e0a8070cf391b31f69d931e5c840d2/fastapi_cli-0.0.16-py3-none-any.whl", hash = "sha256:addcb6d130b5b9c91adbbf3f2947fe115991495fdb442fe3e51b5fc6327df9f4", size = 12312 }, + { url = "https://files.pythonhosted.org/packages/08/89/5c4eef60524d0fd704eb0706885b82cd5623a43396b94e4a5b17d3a3f516/fastapi_cli-0.0.20-py3-none-any.whl", hash = "sha256:e58b6a0038c0b1532b7a0af690656093dee666201b6b19d3c87175b358e9f783", size = 12390 }, ] [package.optional-dependencies] @@ -948,7 +1006,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.6.0" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastar" }, @@ -960,9 +1018,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/dd/e5890bb4ee63f9d8988660b755490e346cf5769aaa7f5f3ced9afb9f090a/fastapi_cloud_cli-0.6.0.tar.gz", hash = "sha256:2c333fff2e4b93b9efbec7896ce3ffa3e77ce4cf3d8cb14e35b4f823dfddac02", size = 30579 } +sdist = { url = "https://files.pythonhosted.org/packages/51/5d/3b33438de35521fab4968b232caa9a4bd568a5078f2b2dfb7bb8a4528603/fastapi_cloud_cli-0.8.0.tar.gz", hash = "sha256:cf07c502528bfd9e6b184776659f05d9212811d76bbec9fbb6bf34bed4c7456f", size = 30257 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/2f/5ba9b5faa75067e30ff48e3c454263ebc2d2301d5509cfefe12cf9fc8156/fastapi_cloud_cli-0.6.0-py3-none-any.whl", hash = "sha256:b654890b5302c90d2f347b123a35186096328838a526316c470b6005cabd4983", size = 23215 }, + { url = "https://files.pythonhosted.org/packages/dd/8e/abb95ef59e91bb5adaa2d18fbf9ea70fd524010bb03f406a2dd2a4775ef9/fastapi_cloud_cli-0.8.0-py3-none-any.whl", hash = "sha256:e9f40bee671d985fd25d7a5409b56d4f103777bf8a0c6d746ea5fbf97a8186d9", size = 22306 }, ] [[package]] @@ -1029,11 +1087,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.20.0" +version = "3.20.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922 } +sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 }, + { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697 }, ] [[package]] @@ -1093,21 +1151,51 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, ] +[[package]] +name = "flask" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/6d/cfe3c0fcc5e477df242b98bfe186a4c34357b4847e87ecaef04507332dab/flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", size = 720160 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308 }, +] + +[[package]] +name = "flask-cors" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257 }, +] + [[package]] name = "fonttools" -version = "4.61.0" +version = "4.61.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/f9/0e84d593c0e12244150280a630999835a64f2852276161b62a0f98318de0/fonttools-4.61.0.tar.gz", hash = "sha256:ec520a1f0c7758d7a858a00f090c1745f6cde6a7c5e76fb70ea4044a15f712e7", size = 3561884 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756 } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/f3/91bba2721fb173fc68e09d15b6ccf3ad4f83d127fbff579be7e5984888a6/fonttools-4.61.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dc25a4a9c1225653e4431a9413d0381b1c62317b0f543bdcec24e1991f612f33", size = 2850151 }, - { url = "https://files.pythonhosted.org/packages/f5/8c/a1691dec01038ac7e7bb3ab83300dcc5087b11d8f48640928c02a873eb92/fonttools-4.61.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b493c32d2555e9944ec1b911ea649ff8f01a649ad9cba6c118d6798e932b3f0", size = 2389769 }, - { url = "https://files.pythonhosted.org/packages/2d/dd/5bb369a44319d92ba25612511eb8ed2a6fa75239979e0388907525626902/fonttools-4.61.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad751319dc532a79bdf628b8439af167181b4210a0cd28a8935ca615d9fdd727", size = 4893189 }, - { url = "https://files.pythonhosted.org/packages/5e/02/51373fa8846bd22bb54e5efb30a824b417b058083f775a194a432f21a45f/fonttools-4.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2de14557d113faa5fb519f7f29c3abe4d69c17fe6a5a2595cc8cda7338029219", size = 4854415 }, - { url = "https://files.pythonhosted.org/packages/8b/64/9cdbbb804577a7e6191448851c57e6a36eb02aa4bf6a9668b528c968e44e/fonttools-4.61.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:59587bbe455dbdf75354a9dbca1697a35a8903e01fab4248d6b98a17032cee52", size = 4870927 }, - { url = "https://files.pythonhosted.org/packages/92/68/e40b22919dc96dc30a70b58fec609ab85112de950bdecfadf8dd478c5a88/fonttools-4.61.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:46cb3d9279f758ac0cf671dc3482da877104b65682679f01b246515db03dbb72", size = 4988674 }, - { url = "https://files.pythonhosted.org/packages/9b/5c/e857349ce8aedb2451b9448282e86544b2b7f1c8b10ea0fe49b7cb369b72/fonttools-4.61.0-cp310-cp310-win32.whl", hash = "sha256:58b4f1b78dfbfe855bb8a6801b31b8cdcca0e2847ec769ad8e0b0b692832dd3b", size = 1497663 }, - { url = "https://files.pythonhosted.org/packages/f9/0c/62961d5fe6f764d6cbc387ef2c001f5f610808c7aded837409836c0b3e7c/fonttools-4.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:68704a8bbe0b61976262b255e90cde593dc0fe3676542d9b4d846bad2a890a76", size = 1546143 }, - { url = "https://files.pythonhosted.org/packages/0c/14/634f7daea5ffe6a5f7a0322ba8e1a0e23c9257b80aa91458107896d1dfc7/fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635", size = 1144485 }, + { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799 }, + { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032 }, + { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863 }, + { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076 }, + { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623 }, + { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327 }, + { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180 }, + { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654 }, + { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996 }, ] [[package]] @@ -1172,27 +1260,26 @@ wheels = [ [[package]] name = "gdown" -version = "4.6.0" +version = "5.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "filelock" }, { name = "requests", extra = ["socks"] }, - { name = "six" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/a8/eeda0086e118c5cc725c549aadadffad4d8d508781add1bd3347f8ad7808/gdown-4.6.0.tar.gz", hash = "sha256:5ce3db0aeda54f46caacb2df86f31c3e3ecd17c355689e6456d85fb528ba9749", size = 14165 } +sdist = { url = "https://files.pythonhosted.org/packages/09/6a/37e6b70c5bda3161e40265861e63b64a86bfc6ca6a8f1c35328a675c84fd/gdown-5.2.0.tar.gz", hash = "sha256:2145165062d85520a3cd98b356c9ed522c5e7984d408535409fd46f94defc787", size = 284647 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/54/0bbe240c6f59ac9209c2356dc65abd209963851569494376a077e55ef98d/gdown-4.6.0-py3-none-any.whl", hash = "sha256:e75c5aa8be8ea1cac642d4793f884339d887ab5e07aaa57fafa16c8a56a0cde5", size = 14400 }, + { url = "https://files.pythonhosted.org/packages/54/70/e07c381e6488a77094f04c85c9caf1c8008cdc30778f7019bc52e5285ef0/gdown-5.2.0-py3-none-any.whl", hash = "sha256:33083832d82b1101bdd0e9df3edd0fbc0e1c5f14c9d8c38d2a35bf1683b526d6", size = 18235 }, ] [[package]] name = "gepa" -version = "0.0.17" +version = "0.0.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/f0/fe312ed4405ddc2ca97dc1ce8915c4dd707e413503e6832910ab088fceb6/gepa-0.0.17.tar.gz", hash = "sha256:641ed46f8127618341b66ee82a87fb46a21c5d2d427a5e0b91c850a7f7f64e7f", size = 99816 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/be/9a4c31c65c4910e73bd4a316df99347681bce4f2ef6d10877cc1ed8d57da/gepa-0.0.22.tar.gz", hash = "sha256:0b13a644efd2af52186e456eaad2ae28fcf88c6f796a9c999910c587863d4315", size = 116337 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/dc/2bc81a01caa887ed58db3c725bebf1e98f37807a4d06c51ecaa85a7cabe0/gepa-0.0.17-py3-none-any.whl", hash = "sha256:0ea98f4179dbc8dd83bdf53494f302e663ee1da8300d086c4cc8ce4aefa4042c", size = 110464 }, + { url = "https://files.pythonhosted.org/packages/b3/0e/d41272b28f2163f5bf840b508cbaf4a0bc01eaeaa6e5d447558d1050eb3b/gepa-0.0.22-py3-none-any.whl", hash = "sha256:ff57ee0442399a5cc62d01411935476bc80cfa8f1c318bed82e3db4c2c834665", size = 119666 }, ] [[package]] @@ -1209,14 +1296,14 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.45" +version = "3.1.46" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076 } +sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371 } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168 }, + { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620 }, ] [[package]] @@ -1237,16 +1324,15 @@ wheels = [ [[package]] name = "google-auth" -version = "2.43.0" +version = "2.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359 } +sdist = { url = "https://files.pythonhosted.org/packages/60/3c/ec64b9a275ca22fa1cd3b6e77fefcf837b0732c890aa32d2bd21313d9b33/google_auth-2.47.0.tar.gz", hash = "sha256:833229070a9dfee1a353ae9877dcd2dec069a8281a4e72e72f77d4a70ff945da", size = 323719 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114 }, + { url = "https://files.pythonhosted.org/packages/db/18/79e9008530b79527e0d5f79e7eef08d3b179b7f851cfd3a2f27822fbdfa9/google_auth-2.47.0-py3-none-any.whl", hash = "sha256:c516d68336bfde7cf0da26aab674a36fedcf04b37ac4edd59c597178760c3498", size = 234867 }, ] [package.optional-dependencies] @@ -1269,7 +1355,7 @@ wheels = [ [[package]] name = "google-cloud-storage" -version = "3.6.0" +version = "3.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -1279,44 +1365,43 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/cd/7e112cf025b2b591067b599e4bfe965df0c12b0cc0afdb5556469bff126d/google_cloud_storage-3.6.0.tar.gz", hash = "sha256:29cc6b9a6c0fc9cdad071e375d540a5a50fbc9a7fad8300fa02fb904f6fe2ca2", size = 17251072 } +sdist = { url = "https://files.pythonhosted.org/packages/d2/8e/fab2de1a0ab7fdbd452eaae5a9a5c933d0911c26b04efa0c76ddfd921259/google_cloud_storage-3.7.0.tar.gz", hash = "sha256:9ce59c65f4d6e372effcecc0456680a8d73cef4f2dc9212a0704799cb3d69237", size = 17258914 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/ef/3b57bf617ee0c79450c1ff211d1eb888db8fc1050ac74b3e52cc6ed86e63/google_cloud_storage-3.6.0-py3-none-any.whl", hash = "sha256:5decbdddd63b7d1fc3e266a393ad6453d2e27d172bd982b1e2f15481668db097", size = 299039 }, + { url = "https://files.pythonhosted.org/packages/2d/80/6e5c7c83cea15ed4dfc4843b9df9db0716bc551ac938f7b5dd18a72bd5e4/google_cloud_storage-3.7.0-py3-none-any.whl", hash = "sha256:469bc9540936e02f8a4bfd1619e9dca1e42dec48f95e4204d783b36476a15093", size = 303364 }, ] [[package]] name = "google-crc32c" -version = "1.7.1" +version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495 } +sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/69/b1b05cf415df0d86691d6a8b4b7e60ab3a6fb6efb783ee5cd3ed1382bfd3/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76", size = 30467 }, - { url = "https://files.pythonhosted.org/packages/44/3d/92f8928ecd671bd5b071756596971c79d252d09b835cdca5a44177fa87aa/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d", size = 30311 }, - { url = "https://files.pythonhosted.org/packages/33/42/c2d15a73df79d45ed6b430b9e801d0bd8e28ac139a9012d7d58af50a385d/google_crc32c-1.7.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c67ca0a1f5b56162951a9dae987988679a7db682d6f97ce0f6381ebf0fbea4c", size = 37889 }, - { url = "https://files.pythonhosted.org/packages/57/ea/ac59c86a3c694afd117bb669bde32aaf17d0de4305d01d706495f09cbf19/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc5319db92daa516b653600794d5b9f9439a9a121f3e162f94b0e1891c7933cb", size = 33028 }, - { url = "https://files.pythonhosted.org/packages/60/44/87e77e8476767a4a93f6cf271157c6d948eacec63688c093580af13b04be/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcdf5a64adb747610140572ed18d011896e3b9ae5195f2514b7ff678c80f1603", size = 38026 }, - { url = "https://files.pythonhosted.org/packages/c8/bf/21ac7bb305cd7c1a6de9c52f71db0868e104a5b573a4977cd9d0ff830f82/google_crc32c-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:754561c6c66e89d55754106739e22fdaa93fafa8da7221b29c8b8e8270c6ec8a", size = 33476 }, - { url = "https://files.pythonhosted.org/packages/0b/43/31e57ce04530794917dfe25243860ec141de9fadf4aa9783dffe7dac7c39/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e9afc74168b0b2232fb32dd202c93e46b7d5e4bf03e66ba5dc273bb3559589", size = 28242 }, - { url = "https://files.pythonhosted.org/packages/eb/f3/8b84cd4e0ad111e63e30eb89453f8dd308e3ad36f42305cf8c202461cdf0/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa8136cc14dd27f34a3221c0f16fd42d8a40e4778273e61a3c19aedaa44daf6b", size = 28049 }, + { url = "https://files.pythonhosted.org/packages/95/ac/6f7bc93886a823ab545948c2dd48143027b2355ad1944c7cf852b338dc91/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff", size = 31296 }, + { url = "https://files.pythonhosted.org/packages/f7/97/a5accde175dee985311d949cfcb1249dcbb290f5ec83c994ea733311948f/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288", size = 30870 }, + { url = "https://files.pythonhosted.org/packages/3d/63/bec827e70b7a0d4094e7476f863c0dbd6b5f0f1f91d9c9b32b76dcdfeb4e/google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d", size = 33214 }, + { url = "https://files.pythonhosted.org/packages/63/bc/11b70614df04c289128d782efc084b9035ef8466b3d0a8757c1b6f5cf7ac/google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092", size = 33589 }, + { url = "https://files.pythonhosted.org/packages/3e/00/a08a4bc24f1261cc5b0f47312d8aebfbe4b53c2e6307f1b595605eed246b/google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733", size = 34437 }, ] [[package]] name = "google-genai" -version = "1.53.0" +version = "1.56.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, + { name = "distro" }, { name = "google-auth", extra = ["requests"] }, { name = "httpx" }, { name = "pydantic" }, { name = "requests" }, + { name = "sniffio" }, { name = "tenacity" }, { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/b3/36fbfde2e21e6d3bc67780b61da33632f495ab1be08076cf0a16af74098f/google_genai-1.53.0.tar.gz", hash = "sha256:938a26d22f3fd32c6eeeb4276ef204ef82884e63af9842ce3eac05ceb39cbd8d", size = 260102 } +sdist = { url = "https://files.pythonhosted.org/packages/70/ad/d3ac5a102135bd3f1e4b1475ca65d2bd4bcc22eb2e9348ac40fe3fadb1d6/google_genai-1.56.0.tar.gz", hash = "sha256:0491af33c375f099777ae207d9621f044e27091fafad4c50e617eba32165e82f", size = 340451 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/f2/97fefdd1ad1f3428321bac819ae7a83ccc59f6439616054736b7819fa56c/google_genai-1.53.0-py3-none-any.whl", hash = "sha256:65a3f99e5c03c372d872cda7419f5940e723374bb12a2f3ffd5e3e56e8eb2094", size = 262015 }, + { url = "https://files.pythonhosted.org/packages/84/93/94bc7a89ef4e7ed3666add55cd859d1483a22737251df659bf1aa46e9405/google_genai-1.56.0-py3-none-any.whl", hash = "sha256:9e6b11e0c105ead229368cb5849a480e4d0185519f8d9f538d61ecfcf193b052", size = 426563 }, ] [[package]] @@ -1333,14 +1418,50 @@ wheels = [ [[package]] name = "googleapis-common-protos" -version = "1.63.1" +version = "1.72.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/bc/cb5c74fca58d9c37bc621642e2c2b19c004d078b472d49fb03d9fa8ffeef/googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a", size = 121632 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433 } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/87/1608d23bb9879694579fff5dc56d60e3d48e012fd08670f140cf82f6cf26/googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877", size = 229151 }, + { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515 }, +] + +[[package]] +name = "graphene" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "graphql-core" }, + { name = "graphql-relay" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894 }, +] + +[[package]] +name = "graphql-core" +version = "3.2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/9b/037a640a2983b09aed4a823f9cf1729e6d780b0671f854efa4727a7affbe/graphql_core-3.2.7.tar.gz", hash = "sha256:27b6904bdd3b43f2a0556dad5d579bdfdeab1f38e8e8788e555bdcb586a6f62c", size = 513484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/14/933037032608787fb92e365883ad6a741c235e0ff992865ec5d904a38f1e/graphql_core-3.2.7-py3-none-any.whl", hash = "sha256:17fc8f3ca4a42913d8e24d9ac9f08deddf0a0b2483076575757f6c412ead2ec0", size = 207262 }, +] + +[[package]] +name = "graphql-relay" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "graphql-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/13/98fbf8d67552f102488ffc16c6f559ce71ea15f6294728d33928ab5ff14d/graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c", size = 50027 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/16/a4cf06adbc711bd364a73ce043b0b08d8fa5aae3df11b6ee4248bcdad2e0/graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5", size = 16940 }, ] [[package]] @@ -1359,6 +1480,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740 }, ] +[[package]] +name = "gunicorn" +version = "23.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 }, +] + [[package]] name = "h11" version = "0.16.0" @@ -1426,6 +1559,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] +[[package]] +name = "huey" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/29/3428d52eb8e85025e264a291641a9f9d6407cc1e51d1b630f6ac5815999a/huey-2.6.0.tar.gz", hash = "sha256:8d11f8688999d65266af1425b831f6e3773e99415027177b8734b0ffd5e251f6", size = 221068 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/34/fae9ac8f1c3a552fd3f7ff652b94c78d219dedc5fce0c0a4232457760a00/huey-2.6.0-py3-none-any.whl", hash = "sha256:1b9df9d370b49c6d5721ba8a01ac9a787cf86b3bdc584e4679de27b920395c3f", size = 76951 }, +] + [[package]] name = "huggingface-hub" version = "0.36.0" @@ -1465,24 +1607,27 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656 }, + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, ] [[package]] name = "intervaltree" -version = "3.1.0" +version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/fb/396d568039d21344639db96d940d40eb62befe704ef849b27949ded5c3bb/intervaltree-3.1.0.tar.gz", hash = "sha256:902b1b88936918f9b2a19e0e5eb7ccb430ae45cde4f39ea4b36932920d33952d", size = 32861 } +sdist = { url = "https://files.pythonhosted.org/packages/53/c3/b2afa612aa0373f3e6bb190e6de35f293b307d1537f109e3e25dbfcdf212/intervaltree-3.2.1.tar.gz", hash = "sha256:f3f7e8baeb7dd75b9f7a6d33cf3ec10025984a8e66e3016d537e52130c73cfe2", size = 1231531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/7f/8a80a1c7c2ed05822b5a2b312d2995f30c533641f8198366ba2e26a7bb03/intervaltree-3.2.1-py2.py3-none-any.whl", hash = "sha256:a8a8381bbd35d48ceebee932c77ffc988492d22fb1d27d0ba1d74a7694eb8f0b", size = 25929 }, +] [[package]] name = "ipykernel" @@ -1510,7 +1655,7 @@ wheels = [ [[package]] name = "ipython" -version = "8.37.0" +version = "8.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, @@ -1525,9 +1670,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996 } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864 }, + { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813 }, ] [[package]] @@ -1558,6 +1703,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, ] +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 }, +] + [[package]] name = "jedi" version = "0.19.2" @@ -1634,29 +1788,29 @@ wheels = [ [[package]] name = "joblib" -version = "1.5.2" +version = "1.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/5d/447af5ea094b9e4c4054f82e223ada074c552335b9b4b2d14bd9b35a67c4/joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55", size = 331077 } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241", size = 308396 }, + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, ] [[package]] name = "json-repair" -version = "0.54.2" +version = "0.55.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/05/9fbcd5ffab9c41455e7d80af65a90876718b8ea2fb4525e187ab11836dd4/json_repair-0.54.2.tar.gz", hash = "sha256:4b6b62ce17f1a505b220fa4aadba1fc37dc9c221544f158471efe3775620bad6", size = 38575 } +sdist = { url = "https://files.pythonhosted.org/packages/96/74/0f39677fa7c0127129c3f1a37c94d05c30a968ba3047200e54dea375b09a/json_repair-0.55.0.tar.gz", hash = "sha256:9fafb47d92582ef4bdd3520656bdb0fcb37b46cf6aa99c1926b7895abc0a3a4b", size = 38828 } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/3a/1b4df9adcd69fee9c9e4b439c13e8c866f2fae520054aede7030b2278be9/json_repair-0.54.2-py3-none-any.whl", hash = "sha256:be51cce5dca97e0c24ebdf61a1ede2449a8a7666012de99467bb7b0afb35179b", size = 29322 }, + { url = "https://files.pythonhosted.org/packages/bc/00/d7d6b6b3257f9b1f997a558b6f7087b8af7c0a2f525f4fbd864c267a88ab/json_repair-0.55.0-py3-none-any.whl", hash = "sha256:bcf4880f5e6ad21a0f70ab034e3d1d398c2ae9698dc5717d7015afbac77b8ed7", size = 29570 }, ] [[package]] name = "json5" -version = "0.12.1" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/ae/929aee9619e9eba9015207a9d2c1c54db18311da7eb4dcf6d41ad6f0eb67/json5-0.12.1.tar.gz", hash = "sha256:b2743e77b3242f8d03c143dd975a6ec7c52e2f2afe76ed934e53503dd4ad4990", size = 52191 } +sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/e2/05328bd2621be49a6fed9e3030b1e51a2d04537d3f816d211b9cc53c5262/json5-0.12.1-py3-none-any.whl", hash = "sha256:d9c9b3bc34a5f54d43c35e11ef7cb87d8bdd098c6ace87117a7b7e83e705c1d5", size = 36119 }, + { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163 }, ] [[package]] @@ -1682,7 +1836,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1690,9 +1844,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040 }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, ] [package.optional-dependencies] @@ -1739,7 +1893,7 @@ wheels = [ [[package]] name = "jupyter-client" -version = "8.6.3" +version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-core" }, @@ -1748,9 +1902,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/27/d10de45e8ad4ce872372c4a3a37b7b35b6b064f6f023a5c14ffcced4d59d/jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d", size = 344691 } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105 }, + { url = "https://files.pythonhosted.org/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0", size = 106215 }, ] [[package]] @@ -1861,7 +2015,7 @@ wheels = [ [[package]] name = "jupyterlab" -version = "4.5.0" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1879,9 +2033,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/e5/4fa382a796a6d8e2cd867816b64f1ff27f906e43a7a83ad9eb389e448cd8/jupyterlab-4.5.0.tar.gz", hash = "sha256:aec33d6d8f1225b495ee2cf20f0514f45e6df8e360bdd7ac9bace0b7ac5177ea", size = 23989880 } +sdist = { url = "https://files.pythonhosted.org/packages/09/21/413d142686a4e8f4268d985becbdb4daf060524726248e73be4773786987/jupyterlab-4.5.1.tar.gz", hash = "sha256:09da1ddfbd9eec18b5101dbb8515612aa1e47443321fb99503725a88e93d20d9", size = 23992251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/1e/5a4d5498eba382fee667ed797cf64ae5d1b13b04356df62f067f48bb0f61/jupyterlab-4.5.0-py3-none-any.whl", hash = "sha256:88e157c75c1afff64c7dc4b801ec471450b922a4eae4305211ddd40da8201c8a", size = 12380641 }, + { url = "https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-py3-none-any.whl", hash = "sha256:31b059de96de0754ff1f2ce6279774b6aab8c34d7082e9752db58207c99bd514", size = 12384821 }, ] [[package]] @@ -2061,14 +2215,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913 }, ] -[[package]] -name = "magicattr" -version = "0.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/7e/76b7e0c391bee7e9273725c29c8fe41c4df62a215ce58aa8e3518baee0bb/magicattr-0.1.6-py2.py3-none-any.whl", hash = "sha256:d96b18ee45b5ee83b09c17e15d3459a64de62d538808c2f71182777dd9dbbbdf", size = 4664 }, -] - [[package]] name = "mako" version = "1.3.10" @@ -2168,7 +2314,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.7" +version = "3.10.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -2181,17 +2327,17 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/e2/d2d5295be2f44c678ebaf3544ba32d20c1f9ef08c49fe47f496180e1db15/matplotlib-3.10.7.tar.gz", hash = "sha256:a06ba7e2a2ef9131c79c49e63dad355d2d878413a0376c1727c8b9335ff731c7", size = 34804865 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/87/3932d5778ab4c025db22710b61f49ccaed3956c5cf46ffb2ffa7492b06d9/matplotlib-3.10.7-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ac81eee3b7c266dd92cee1cd658407b16c57eed08c7421fa354ed68234de380", size = 8247141 }, - { url = "https://files.pythonhosted.org/packages/45/a8/bfed45339160102bce21a44e38a358a1134a5f84c26166de03fb4a53208f/matplotlib-3.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:667ecd5d8d37813a845053d8f5bf110b534c3c9f30e69ebd25d4701385935a6d", size = 8107995 }, - { url = "https://files.pythonhosted.org/packages/e2/3c/5692a2d9a5ba848fda3f48d2b607037df96460b941a59ef236404b39776b/matplotlib-3.10.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc1c51b846aca49a5a8b44fbba6a92d583a35c64590ad9e1e950dc88940a4297", size = 8680503 }, - { url = "https://files.pythonhosted.org/packages/ab/a0/86ace53c48b05d0e6e9c127b2ace097434901f3e7b93f050791c8243201a/matplotlib-3.10.7-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a11c2e9e72e7de09b7b72e62f3df23317c888299c875e2b778abf1eda8c0a42", size = 9514982 }, - { url = "https://files.pythonhosted.org/packages/a6/81/ead71e2824da8f72640a64166d10e62300df4ae4db01a0bac56c5b39fa51/matplotlib-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f19410b486fdd139885ace124e57f938c1e6a3210ea13dd29cab58f5d4bc12c7", size = 9566429 }, - { url = "https://files.pythonhosted.org/packages/65/7d/954b3067120456f472cce8fdcacaf4a5fcd522478db0c37bb243c7cb59dd/matplotlib-3.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:b498e9e4022f93de2d5a37615200ca01297ceebbb56fe4c833f46862a490f9e3", size = 8108174 }, - { url = "https://files.pythonhosted.org/packages/1e/6c/a9bcf03e9afb2a873e0a5855f79bce476d1023f26f8212969f2b7504756c/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5c09cf8f2793f81368f49f118b6f9f937456362bee282eac575cca7f84cda537", size = 8241204 }, - { url = "https://files.pythonhosted.org/packages/5b/fd/0e6f5aa762ed689d9fa8750b08f1932628ffa7ed30e76423c399d19407d2/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:de66744b2bb88d5cd27e80dfc2ec9f0517d0a46d204ff98fe9e5f2864eb67657", size = 8104607 }, - { url = "https://files.pythonhosted.org/packages/b9/a9/21c9439d698fac5f0de8fc68b2405b738ed1f00e1279c76f2d9aa5521ead/matplotlib-3.10.7-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53cc80662dd197ece414dd5b66e07370201515a3eaf52e7c518c68c16814773b", size = 8682257 }, + { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828 }, + { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050 }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452 }, + { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928 }, + { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377 }, + { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127 }, + { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252 }, + { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693 }, + { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205 }, ] [[package]] @@ -2217,14 +2363,14 @@ wheels = [ [[package]] name = "mistune" -version = "3.1.4" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/02/a7fb8b21d4d55ac93cdcde9d3638da5dd0ebdd3a4fed76c7725e10b81cbe/mistune-3.1.4.tar.gz", hash = "sha256:b5a7f801d389f724ec702840c11d8fc48f2b33519102fc7ee739e8177b672164", size = 94588 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/f0/8282d9641415e9e33df173516226b404d367a0fc55e1a60424a152913abc/mistune-3.1.4-py3-none-any.whl", hash = "sha256:93691da911e5d9d2e23bc54472892aff676df27a75274962ff9edc210364266d", size = 53481 }, + { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598 }, ] [[package]] @@ -2240,6 +2386,84 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, ] +[[package]] +name = "mlflow" +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alembic" }, + { name = "cryptography" }, + { name = "docker" }, + { name = "flask" }, + { name = "flask-cors" }, + { name = "graphene" }, + { name = "gunicorn", marker = "platform_system != 'Windows'" }, + { name = "huey" }, + { name = "matplotlib" }, + { name = "mlflow-skinny" }, + { name = "mlflow-tracing" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "sqlalchemy" }, + { name = "waitress", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/f8/10ccb111ed53732dfceae0369073023f96acd6b00f92fb3c24473938702d/mlflow-3.8.1.tar.gz", hash = "sha256:0823377bedff4d530b0d560bf394daf9f7e9fbba53453add04eadad34de962cc", size = 8550037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/d5/a20b87c6cd99395fee04d6034686512305530c71ceaabe3a151eeaa25ed7/mlflow-3.8.1-py3-none-any.whl", hash = "sha256:42f26b52438fdb615588e150407c6516d0f64d417436dfc75599c525a464f210", size = 9062281 }, +] + +[[package]] +name = "mlflow-skinny" +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "click" }, + { name = "cloudpickle" }, + { name = "databricks-sdk" }, + { name = "fastapi" }, + { name = "gitpython" }, + { name = "importlib-metadata" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sqlparse" }, + { name = "typing-extensions" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/00/18486d9072739e63471c1e441e78cdb6a10c641312d98f6699715406451e/mlflow_skinny-3.8.1.tar.gz", hash = "sha256:0c0aade08187030a4653e267bcd63de2f12cbfebf4c6737832cba45d6fb3594d", size = 2082226 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/24/42e52320636fcbabeaf50704f9269a328acc995e1b8a44df6fea33130a0a/mlflow_skinny-3.8.1-py3-none-any.whl", hash = "sha256:3a6ee27f5ac1e67c1d565fa0e12c070b27129b03e669dcaf88ff841176429142", size = 2506002 }, +] + +[[package]] +name = "mlflow-tracing" +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "databricks-sdk" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/e5/9cdca5a91afd71e15943f3dbe00e788bd36479637e9b2e0625224027ff01/mlflow_tracing-3.8.1.tar.gz", hash = "sha256:c032ba715994a4580323f3045fa2700a6323033d87e564bbcbda37e6ab993071", size = 1130700 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/e3/ddbe3e2d1219fa9235559ab88ebf98e1e4f48c62672f0dfb8f1eb07276dc/mlflow_tracing-3.8.1-py3-none-any.whl", hash = "sha256:12d9b5b7177b4152979d003e0d967b280c4252758639aebdfd672734283b17bf", size = 1359007 }, +] + [[package]] name = "more-itertools" version = "10.8.0" @@ -2320,16 +2544,16 @@ wheels = [ [[package]] name = "narwhals" -version = "2.13.0" +version = "2.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/ea/f82ef99ced4d03c33bb314c9b84a08a0a86c448aaa11ffd6256b99538aa5/narwhals-2.13.0.tar.gz", hash = "sha256:ee94c97f4cf7cfeebbeca8d274784df8b3d7fd3f955ce418af998d405576fdd9", size = 594555 } +sdist = { url = "https://files.pythonhosted.org/packages/47/6d/b57c64e5038a8cf071bce391bb11551657a74558877ac961e7fa905ece27/narwhals-2.15.0.tar.gz", hash = "sha256:a9585975b99d95084268445a1fdd881311fa26ef1caa18020d959d5b2ff9a965", size = 603479 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/0d/1861d1599571974b15b025e12b142d8e6b42ad66c8a07a89cb0fc21f1e03/narwhals-2.13.0-py3-none-any.whl", hash = "sha256:9b795523c179ca78204e3be53726da374168f906e38de2ff174c2363baaaf481", size = 426407 }, + { url = "https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.whl", hash = "sha256:cbfe21ca19d260d9fd67f995ec75c44592d1f106933b03ddd375df7ac841f9d6", size = 432856 }, ] [[package]] name = "nbclient" -version = "0.10.2" +version = "0.10.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-client" }, @@ -2337,9 +2561,9 @@ dependencies = [ { name = "nbformat" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/66/7ffd18d58eae90d5721f9f39212327695b749e23ad44b3881744eaf4d9e8/nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193", size = 62424 } +sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d", size = 25434 }, + { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465 }, ] [[package]] @@ -2414,16 +2638,16 @@ wheels = [ [[package]] name = "nodeenv" -version = "1.9.1" +version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438 }, ] [[package]] name = "notebook" -version = "7.5.0" +version = "7.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, @@ -2432,9 +2656,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/ac/a97041621250a4fc5af379fb377942841eea2ca146aab166b8fcdfba96c2/notebook-7.5.0.tar.gz", hash = "sha256:3b27eaf9913033c28dde92d02139414c608992e1df4b969c843219acf2ff95e4", size = 14052074 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/a9/882707b0aa639e6d7d3e7df4bfbe07479d832e9a8f02d8471002a4ea6d65/notebook-7.5.1.tar.gz", hash = "sha256:b2fb4cef4d47d08c33aecce1c6c6e84be05436fbd791f88fce8df9fbca088b75", size = 14058696 } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/96/00df2a4760f10f5af0f45c4955573cae6189931f9a30265a35865f8c1031/notebook-7.5.0-py3-none-any.whl", hash = "sha256:3300262d52905ca271bd50b22617681d95f08a8360d099e097726e6d2efb5811", size = 14460968 }, + { url = "https://files.pythonhosted.org/packages/d1/86/ca516cb58ad2cb2064124d31cf0fd8b012fca64bebeb26da2d2ddf03fc79/notebook-7.5.1-py3-none-any.whl", hash = "sha256:f4e2451c19910c33b88709b84537e11f6368c1cdff1aa0c43db701aea535dd44", size = 14468080 }, ] [[package]] @@ -2672,6 +2896,58 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, ] +[[package]] +name = "opentelemetry-api" +version = "1.39.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356 }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.39.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535 }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.39.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565 }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.60b1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982 }, +] + [[package]] name = "optuna" version = "4.6.0" @@ -2692,23 +2968,23 @@ wheels = [ [[package]] name = "orjson" -version = "3.11.4" +version = "3.11.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c6/fe/ed708782d6709cc60eb4c2d8a361a440661f74134675c72990f2c48c785f/orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d", size = 5945188 } +sdist = { url = "https://files.pythonhosted.org/packages/04/b8/333fdb27840f3bf04022d21b654a35f58e15407183aeb16f3b41aa053446/orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5", size = 5972347 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/30/5aed63d5af1c8b02fbd2a8d83e2a6c8455e30504c50dbf08c8b51403d873/orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1", size = 243870 }, - { url = "https://files.pythonhosted.org/packages/44/1f/da46563c08bef33c41fd63c660abcd2184b4d2b950c8686317d03b9f5f0c/orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44", size = 130622 }, - { url = "https://files.pythonhosted.org/packages/02/bd/b551a05d0090eab0bf8008a13a14edc0f3c3e0236aa6f5b697760dd2817b/orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c", size = 129344 }, - { url = "https://files.pythonhosted.org/packages/87/6c/9ddd5e609f443b2548c5e7df3c44d0e86df2c68587a0e20c50018cdec535/orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23", size = 136633 }, - { url = "https://files.pythonhosted.org/packages/95/f2/9f04f2874c625a9fb60f6918c33542320661255323c272e66f7dcce14df2/orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea", size = 137695 }, - { url = "https://files.pythonhosted.org/packages/d2/c2/c7302afcbdfe8a891baae0e2cee091583a30e6fa613e8bdf33b0e9c8a8c7/orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba", size = 136879 }, - { url = "https://files.pythonhosted.org/packages/c6/3a/b31c8f0182a3e27f48e703f46e61bb769666cd0dac4700a73912d07a1417/orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff", size = 136374 }, - { url = "https://files.pythonhosted.org/packages/29/d0/fd9ab96841b090d281c46df566b7f97bc6c8cd9aff3f3ebe99755895c406/orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac", size = 140519 }, - { url = "https://files.pythonhosted.org/packages/d6/ce/36eb0f15978bb88e33a3480e1a3fb891caa0f189ba61ce7713e0ccdadabf/orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79", size = 406522 }, - { url = "https://files.pythonhosted.org/packages/85/11/e8af3161a288f5c6a00c188fc729c7ba193b0cbc07309a1a29c004347c30/orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827", size = 149790 }, - { url = "https://files.pythonhosted.org/packages/ea/96/209d52db0cf1e10ed48d8c194841e383e23c2ced5a2ee766649fe0e32d02/orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b", size = 140040 }, - { url = "https://files.pythonhosted.org/packages/ef/0e/526db1395ccb74c3d59ac1660b9a325017096dc5643086b38f27662b4add/orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3", size = 135955 }, - { url = "https://files.pythonhosted.org/packages/e6/69/18a778c9de3702b19880e73c9866b91cc85f904b885d816ba1ab318b223c/orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc", size = 131577 }, + { url = "https://files.pythonhosted.org/packages/79/19/b22cf9dad4db20c8737041046054cbd4f38bb5a2d0e4bb60487832ce3d76/orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1", size = 245719 }, + { url = "https://files.pythonhosted.org/packages/03/2e/b136dd6bf30ef5143fbe76a4c142828b55ccc618be490201e9073ad954a1/orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870", size = 132467 }, + { url = "https://files.pythonhosted.org/packages/ae/fc/ae99bfc1e1887d20a0268f0e2686eb5b13d0ea7bbe01de2b566febcd2130/orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09", size = 130702 }, + { url = "https://files.pythonhosted.org/packages/6e/43/ef7912144097765997170aca59249725c3ab8ef6079f93f9d708dd058df5/orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd", size = 135907 }, + { url = "https://files.pythonhosted.org/packages/3f/da/24d50e2d7f4092ddd4d784e37a3fa41f22ce8ed97abc9edd222901a96e74/orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac", size = 139935 }, + { url = "https://files.pythonhosted.org/packages/02/4a/b4cb6fcbfff5b95a3a019a8648255a0fac9b221fbf6b6e72be8df2361feb/orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e", size = 137541 }, + { url = "https://files.pythonhosted.org/packages/a5/99/a11bd129f18c2377c27b2846a9d9be04acec981f770d711ba0aaea563984/orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f", size = 139031 }, + { url = "https://files.pythonhosted.org/packages/64/29/d7b77d7911574733a036bb3e8ad7053ceb2b7d6ea42208b9dbc55b23b9ed/orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18", size = 141622 }, + { url = "https://files.pythonhosted.org/packages/93/41/332db96c1de76b2feda4f453e91c27202cd092835936ce2b70828212f726/orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a", size = 413800 }, + { url = "https://files.pythonhosted.org/packages/76/e1/5a0d148dd1f89ad2f9651df67835b209ab7fcb1118658cf353425d7563e9/orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7", size = 151198 }, + { url = "https://files.pythonhosted.org/packages/0d/96/8db67430d317a01ae5cf7971914f6775affdcfe99f5bff9ef3da32492ecc/orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401", size = 141984 }, + { url = "https://files.pythonhosted.org/packages/71/49/40d21e1aa1ac569e521069228bb29c9b5a350344ccf922a0227d93c2ed44/orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8", size = 135272 }, + { url = "https://files.pythonhosted.org/packages/c4/7e/d0e31e78be0c100e08be64f48d2850b23bcb4d4c70d114f4e43b39f6895a/orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167", size = 133360 }, ] [[package]] @@ -2731,21 +3007,23 @@ wheels = [ [[package]] name = "pandas" -version = "1.5.3" +version = "2.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, { name = "pytz" }, + { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/ee/146cab1ff6d575b54ace8a6a5994048380dc94879b0125b25e62edcb9e52/pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1", size = 5203060 } +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/cd/34f6b0780301be81be804d7aa71d571457369e6131e2b330af2b0fed1aad/pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406", size = 18619230 }, - { url = "https://files.pythonhosted.org/packages/5f/34/b7858bb7d6d6bf4d9df1dde777a11fcf3ff370e1d1b3956e3d0fcca8322c/pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572", size = 11982991 }, - { url = "https://files.pythonhosted.org/packages/b8/6c/005bd604994f7cbede4d7bf030614ef49a2213f76bc3d738ecf5b0dcc810/pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996", size = 10927131 }, - { url = "https://files.pythonhosted.org/packages/27/c7/35b81ce5f680f2dac55eac14d103245cd8cf656ae4a2ff3be2e69fd1d330/pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354", size = 11368188 }, - { url = "https://files.pythonhosted.org/packages/49/e2/79e46612dc25ebc7603dc11c560baa7266c90f9e48537ecf1a02a0dd6bff/pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23", size = 12062104 }, - { url = "https://files.pythonhosted.org/packages/d9/cd/f27c2992cbe05a3e39937f73a4be635a9ec149ec3ca4467d8cf039718994/pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328", size = 10362473 }, + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763 }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217 }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791 }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373 }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444 }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459 }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086 }, ] [[package]] @@ -2877,7 +3155,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d [[package]] name = "pre-commit" -version = "4.5.0" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -2886,9 +3164,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428 } +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429 }, + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437 }, ] [[package]] @@ -2938,27 +3216,29 @@ wheels = [ [[package]] name = "proto-plus" -version = "1.26.1" +version = "1.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } +sdist = { url = "https://files.pythonhosted.org/packages/01/89/9cbe2f4bba860e149108b683bc2efec21f14d5f7ed6e25562ad86acbc373/proto_plus-1.27.0.tar.gz", hash = "sha256:873af56dd0d7e91836aee871e5799e1c6f1bda86ac9a983e0bb9f0c266a568c4", size = 56158 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, + { url = "https://files.pythonhosted.org/packages/cd/24/3b7a0818484df9c28172857af32c2397b6d8fcd99d9468bd4684f98ebf0a/proto_plus-1.27.0-py3-none-any.whl", hash = "sha256:1baa7f81cf0f8acb8bc1f6d085008ba4171eaf669629d1b6d1673b21ed1c0a82", size = 50205 }, ] [[package]] name = "protobuf" -version = "3.19.6" +version = "6.33.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/d1/79bfd1f481469b661a2eddab551255536401892722189433282bfb13cfb1/protobuf-3.19.6.tar.gz", hash = "sha256:5f5540d57a43042389e87661c6eaa50f47c19c6176e8cf1c4f287aeefeccb5c4", size = 218071 } +sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/3b/90f805b9e5ecacf8a216f2e5acabc2d3ad965b62803510be41804e6bfbfe/protobuf-3.19.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:010be24d5a44be7b0613750ab40bc8b8cedc796db468eae6c779b395f50d1fa1", size = 913631 }, - { url = "https://files.pythonhosted.org/packages/26/ef/bd6ba3b4ff9a35944bdd325e2c9ee56f71e855757f7d43938232499f0278/protobuf-3.19.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11478547958c2dfea921920617eb457bc26867b0d1aa065ab05f35080c5d9eb6", size = 1055327 }, - { url = "https://files.pythonhosted.org/packages/4a/25/85bcc155980b5d7754ebdf4cb32039105f6020b6b2b8f7536a866113fc1c/protobuf-3.19.6-cp310-cp310-win32.whl", hash = "sha256:559670e006e3173308c9254d63facb2c03865818f22204037ab76f7a0ff70b5f", size = 775745 }, - { url = "https://files.pythonhosted.org/packages/97/f9/a14bac5331f3e55bcbbed906a0c8b112f554152ddf09efeb6f5f95653ffd/protobuf-3.19.6-cp310-cp310-win_amd64.whl", hash = "sha256:347b393d4dd06fb93a77620781e11c058b3b0a5289262f094379ada2920a3730", size = 895657 }, - { url = "https://files.pythonhosted.org/packages/32/27/1141a8232723dcb10a595cc0ce4321dcbbd5215300bf4acfc142343205bf/protobuf-3.19.6-py2.py3-none-any.whl", hash = "sha256:14082457dc02be946f60b15aad35e9f5c69e738f80ebbc0900a19bc83734a5a4", size = 162648 }, + { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603 }, + { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930 }, + { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621 }, + { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460 }, + { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168 }, + { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270 }, + { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501 }, ] [[package]] @@ -3099,6 +3379,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009 }, ] +[[package]] +name = "pydantic-extra-types" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296 }, +] + [[package]] name = "pydantic-settings" version = "2.12.0" @@ -3147,43 +3440,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] -[[package]] -name = "pympler" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "platform_system == 'Windows'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/37/c384631908029676d8e7213dd956bb686af303a80db7afbc9be36bc49495/pympler-1.1.tar.gz", hash = "sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424", size = 179954 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766 }, -] - [[package]] name = "pymupdf" -version = "1.26.6" +version = "1.26.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/a6f0e03a117fa2ad79c4b898203bb212b17804f92558a6a339298faca7bb/pymupdf-1.26.6.tar.gz", hash = "sha256:a2b4531cd4ab36d6f1f794bb6d3c33b49bda22f36d58bb1f3e81cbc10183bd2b", size = 84322494 } +sdist = { url = "https://files.pythonhosted.org/packages/48/d6/09b28f027b510838559f7748807192149c419b30cb90e6d5f0cf916dc9dc/pymupdf-1.26.7.tar.gz", hash = "sha256:71add8bdc8eb1aaa207c69a13400693f06ad9b927bea976f5d5ab9df0bb489c3", size = 84327033 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/5c/dec354eee5fe4966c715f33818ed4193e0e6c986cf8484de35b6c167fb8e/pymupdf-1.26.6-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e46f320a136ad55e5219e8f0f4061bdf3e4c12b126d2740d5a49f73fae7ea176", size = 23178988 }, - { url = "https://files.pythonhosted.org/packages/ec/a0/11adb742d18142bd623556cd3b5d64649816decc5eafd30efc9498657e76/pymupdf-1.26.6-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:6844cd2396553c0fa06de4869d5d5ecb1260e6fc3b9d85abe8fa35f14dd9d688", size = 22469764 }, - { url = "https://files.pythonhosted.org/packages/e4/c8/377cf20e31f58d4c243bfcf2d3cb7466d5b97003b10b9f1161f11eb4a994/pymupdf-1.26.6-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:617ba69e02c44f0da1c0e039ea4a26cf630849fd570e169c71daeb8ac52a81d6", size = 23502227 }, - { url = "https://files.pythonhosted.org/packages/4f/bf/6e02e3d84b32c137c71a0a3dcdba8f2f6e9950619a3bc272245c7c06a051/pymupdf-1.26.6-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7777d0b7124c2ebc94849536b6a1fb85d158df3b9d873935e63036559391534c", size = 24115381 }, - { url = "https://files.pythonhosted.org/packages/ab/9d/30f7fcb3776bfedde66c06297960debe4883b1667294a1ee9426c942e94d/pymupdf-1.26.6-cp310-abi3-win32.whl", hash = "sha256:8f3ef05befc90ca6bb0f12983200a7048d5bff3e1c1edef1bb3de60b32cb5274", size = 17203613 }, - { url = "https://files.pythonhosted.org/packages/f9/e8/989f4eaa369c7166dc24f0eaa3023f13788c40ff1b96701f7047421554a8/pymupdf-1.26.6-cp310-abi3-win_amd64.whl", hash = "sha256:ce02ca96ed0d1acfd00331a4d41a34c98584d034155b06fd4ec0f051718de7ba", size = 18405680 }, + { url = "https://files.pythonhosted.org/packages/94/35/cd74cea1787b2247702ef8522186bdef32e9cb30a099e6bb864627ef6045/pymupdf-1.26.7-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:07085718dfdae5ab83b05eb5eb397f863bcc538fe05135318a01ea353e7a1353", size = 23179369 }, + { url = "https://files.pythonhosted.org/packages/72/74/448b6172927c829c6a3fba80078d7b0a016ebbe2c9ee528821f5ea21677a/pymupdf-1.26.7-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:31aa9c8377ea1eea02934b92f4dcf79fb2abba0bf41f8a46d64c3e31546a3c02", size = 22470101 }, + { url = "https://files.pythonhosted.org/packages/65/e7/47af26f3ac76be7ac3dd4d6cc7ee105948a8355d774e5ca39857bf91c11c/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e419b609996434a14a80fa060adec72c434a1cca6a511ec54db9841bc5d51b3c", size = 23502486 }, + { url = "https://files.pythonhosted.org/packages/2a/6b/3de1714d734ff949be1e90a22375d0598d3540b22ae73eb85c2d7d1f36a9/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:69dfc78f206a96e5b3ac22741263ebab945fdf51f0dbe7c5757c3511b23d9d72", size = 24115727 }, + { url = "https://files.pythonhosted.org/packages/62/9b/f86224847949577a523be2207315ae0fd3155b5d909cd66c274d095349a3/pymupdf-1.26.7-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1d5106f46e1ca0d64d46bd51892372a4f82076bdc14a9678d33d630702abca36", size = 24324386 }, + { url = "https://files.pythonhosted.org/packages/85/8e/a117d39092ca645fde8b903f4a941d9aa75b370a67b4f1f435f56393dc5a/pymupdf-1.26.7-cp310-abi3-win32.whl", hash = "sha256:7c9645b6f5452629c747690190350213d3e5bbdb6b2eca227d82702b327f6eee", size = 17203888 }, + { url = "https://files.pythonhosted.org/packages/dd/c3/d0047678146c294469c33bae167c8ace337deafb736b0bf97b9bc481aa65/pymupdf-1.26.7-cp310-abi3-win_amd64.whl", hash = "sha256:425b1befe40d41b72eb0fe211711c7ae334db5eb60307e9dd09066ed060cceba", size = 18405952 }, ] [[package]] name = "pymupdf4llm" -version = "0.2.6" +version = "0.2.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pymupdf" }, { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/bd/5c1958ac1ab68c12a09aef1128b3522e1ada6086c6cb42f880dcb294b13e/pymupdf4llm-0.2.6.tar.gz", hash = "sha256:e2deb44ba35aef92c4d84254ef70739feb982256235de2740ffb016b25480da4", size = 64787 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/ff/d2e856ff5db5fa57ebf13ae04de4da6c203f5fb17507c0dd21ffd0ae4a17/pymupdf4llm-0.2.8.tar.gz", hash = "sha256:c4f9815cff210cef45123e93ae3a6f802e0dc1d1ad4534a4a463c000aaec98fd", size = 66867 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/fa/2c7d7ce85d7844097d96a8085e719ef5508eaf60e0d0ec24d2d3d42fffc9/pymupdf4llm-0.2.6-py3-none-any.whl", hash = "sha256:0b8f19d867ca3d9f1e0835a376431b74ef799a0bc0bae1442d1d429fde2d355b", size = 66562 }, + { url = "https://files.pythonhosted.org/packages/58/d5/47e14346b6f57cbc7456cc24ded697b4037b399ff56ed12afccbd202361d/pymupdf4llm-0.2.8-py3-none-any.whl", hash = "sha256:e60587d151223fc9e118661a9c7bb04b04ae928187c201a14d60ac8a42963591", size = 68637 }, ] [[package]] @@ -3197,11 +3479,11 @@ wheels = [ [[package]] name = "pyparsing" -version = "3.2.5" +version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274 } +sdist = { url = "https://files.pythonhosted.org/packages/33/c1/1d9de9aeaa1b89b0186e5fe23294ff6517fce1bc69149185577cd31016b2/pyparsing-3.3.1.tar.gz", hash = "sha256:47fad0f17ac1e2cad3de3b458570fbc9b03560aa029ed5e16ee5554da9a2251c", size = 1550512 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890 }, + { url = "https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-py3-none-any.whl", hash = "sha256:023b5e7e5520ad96642e2c6db4cb683d3970bd640cdf7115049a6e9c3682df82", size = 121793 }, ] [[package]] @@ -3278,11 +3560,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.20" +version = "0.0.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } +sdist = { url = "https://files.pythonhosted.org/packages/78/96/804520d0850c7db98e5ccb70282e29208723f0964e88ffd9d0da2f52ea09/python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92", size = 37196 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, + { url = "https://files.pythonhosted.org/packages/aa/76/03af049af4dcee5d27442f71b6924f01f3efb5d2bd34f23fcd563f2cc5f5/python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090", size = 24541 }, ] [[package]] @@ -3525,16 +3807,16 @@ wheels = [ [[package]] name = "rich-toolkit" -version = "0.17.0" +version = "0.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/d0/8f8de36e1abf8339b497ce700dd7251ca465ffca4a1976969b0eaeb596fb/rich_toolkit-0.17.0.tar.gz", hash = "sha256:17ca7a32e613001aa0945ddea27a246f6de01dfc4c12403254c057a8ee542977", size = 187955 } +sdist = { url = "https://files.pythonhosted.org/packages/97/09/3f9b8d9daaf235195c626f21e03604c05b987404ee3bcacee0c1f67f2a8e/rich_toolkit-0.17.1.tar.gz", hash = "sha256:5af54df8d1dd9c8530e462e1bdcaed625c9b49f5a55b035aa0ba1c17bdb87c9a", size = 187925 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/42/ef2ed40699567661d03b0b511ac46cf6cee736de8f3666819c12d6d20696/rich_toolkit-0.17.0-py3-none-any.whl", hash = "sha256:06fb47a5c5259d6b480287cd38aff5f551b6e1a307f90ed592453dd360e4e71e", size = 31412 }, + { url = "https://files.pythonhosted.org/packages/7f/7b/15e55fa8a76d0d41bf34d965af78acdaf80a315907adb30de8b63c272694/rich_toolkit-0.17.1-py3-none-any.whl", hash = "sha256:96d24bb921ecd225ffce7c526a9149e74006410c05e6d405bd74ffd54d5631ed", size = 31412 }, ] [[package]] @@ -3709,20 +3991,19 @@ wheels = [ [[package]] name = "send2trash" -version = "1.8.3" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/3a/aec9b02217bb79b87bbc1a21bc6abc51e3d5dcf65c30487ac96c0908c722/Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf", size = 17394 } +sdist = { url = "https://files.pythonhosted.org/packages/62/6e/421803dec0c0dfbf5a27e66491ebe6643a461e4f90422f00ea4c68ae24aa/send2trash-2.0.0.tar.gz", hash = "sha256:1761421da3f9930bfe51ed7c45343948573383ad4c27e3acebc91be324e7770d", size = 17206 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", size = 18072 }, + { url = "https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl", hash = "sha256:e70d5ce41dbb890882cc78bc25d137478330b39a391e756fadf82e34da4d85b8", size = 17642 }, ] [[package]] name = "sentence-transformers" -version = "5.1.2" +version = "5.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, - { name = "pillow" }, { name = "scikit-learn" }, { name = "scipy" }, { name = "torch" }, @@ -3730,9 +4011,9 @@ dependencies = [ { name = "transformers" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/96/f3f3409179d14dbfdbea8622e2e9eaa3c8836ddcaecd2cd5ff0a11731d20/sentence_transformers-5.1.2.tar.gz", hash = "sha256:0f6c8bd916a78dc65b366feb8d22fd885efdb37432e7630020d113233af2b856", size = 375185 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/a1/64e7b111e753307ffb7c5b6d039c52d4a91a47fa32a7f5bc377a49b22402/sentence_transformers-5.2.0.tar.gz", hash = "sha256:acaeb38717de689f3dab45d5e5a02ebe2f75960a4764ea35fea65f58a4d3019f", size = 381004 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/a6/a607a737dc1a00b7afe267b9bfde101b8cee2529e197e57471d23137d4e5/sentence_transformers-5.1.2-py3-none-any.whl", hash = "sha256:724ce0ea62200f413f1a5059712aff66495bc4e815a1493f7f9bca242414c333", size = 488009 }, + { url = "https://files.pythonhosted.org/packages/40/d0/3b2897ef6a0c0c801e9fecca26bcc77081648e38e8c772885ebdd8d7d252/sentence_transformers-5.2.0-py3-none-any.whl", hash = "sha256:aa57180f053687d29b08206766ae7db549be5074f61849def7b17bf0b8025ca2", size = 493748 }, ] [[package]] @@ -3753,15 +4034,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.47.0" +version = "2.48.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/2a/d225cbf87b6c8ecce5664db7bcecb82c317e448e3b24a2dcdaacb18ca9a7/sentry_sdk-2.47.0.tar.gz", hash = "sha256:8218891d5e41b4ea8d61d2aed62ed10c80e39d9f2959d6f939efbf056857e050", size = 381895 } +sdist = { url = "https://files.pythonhosted.org/packages/40/f0/0e9dc590513d5e742d7799e2038df3a05167cba084c6ca4f3cdd75b55164/sentry_sdk-2.48.0.tar.gz", hash = "sha256:5213190977ff7fdff8a58b722fb807f8d5524a80488626ebeda1b5676c0c1473", size = 384828 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/ac/d6286ea0d49e7b58847faf67b00e56bb4ba3d525281e2ac306e1f1f353da/sentry_sdk-2.47.0-py2.py3-none-any.whl", hash = "sha256:d72f8c61025b7d1d9e52510d03a6247b280094a327dd900d987717a4fce93412", size = 411088 }, + { url = "https://files.pythonhosted.org/packages/4d/19/8d77f9992e5cbfcaa9133c3bf63b4fbbb051248802e1e803fed5c552fbb2/sentry_sdk-2.48.0-py2.py3-none-any.whl", hash = "sha256:6b12ac256769d41825d9b7518444e57fa35b5642df4c7c5e322af4d2c8721172", size = 414555 }, ] [[package]] @@ -3820,32 +4101,31 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.8" +version = "2.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472 } +sdist = { url = "https://files.pythonhosted.org/packages/89/23/adf3796d740536d63a6fbda113d07e60c734b6ed5d3058d1e47fc0495e47/soupsieve-2.8.1.tar.gz", hash = "sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350", size = 117856 } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 }, + { url = "https://files.pythonhosted.org/packages/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl", hash = "sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434", size = 36710 }, ] [[package]] name = "sqlalchemy" -version = "2.0.44" +version = "2.0.45" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830 } +sdist = { url = "https://files.pythonhosted.org/packages/be/f9/5e4491e5ccf42f5d9cfc663741d261b3e6e1683ae7812114e7636409fcc6/sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88", size = 9869912 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a7/e9ccfa7eecaf34c6f57d8cb0bb7cbdeeff27017cc0f5d0ca90fdde7a7c0d/sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce", size = 2137282 }, - { url = "https://files.pythonhosted.org/packages/b1/e1/50bc121885bdf10833a4f65ecbe9fe229a3215f4d65a58da8a181734cae3/sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985", size = 2127322 }, - { url = "https://files.pythonhosted.org/packages/46/f2/a8573b7230a3ce5ee4b961a2d510d71b43872513647398e595b744344664/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0", size = 3214772 }, - { url = "https://files.pythonhosted.org/packages/4a/d8/c63d8adb6a7edaf8dcb6f75a2b1e9f8577960a1e489606859c4d73e7d32b/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e", size = 3214434 }, - { url = "https://files.pythonhosted.org/packages/ee/a6/243d277a4b54fae74d4797957a7320a5c210c293487f931cbe036debb697/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749", size = 3155365 }, - { url = "https://files.pythonhosted.org/packages/5f/f8/6a39516ddd75429fd4ee5a0d72e4c80639fab329b2467c75f363c2ed9751/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2", size = 3178910 }, - { url = "https://files.pythonhosted.org/packages/43/f0/118355d4ad3c39d9a2f5ee4c7304a9665b3571482777357fa9920cd7a6b4/sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165", size = 2105624 }, - { url = "https://files.pythonhosted.org/packages/61/83/6ae5f9466f8aa5d0dcebfff8c9c33b98b27ce23292df3b990454b3d434fd/sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5", size = 2129240 }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718 }, + { url = "https://files.pythonhosted.org/packages/fe/70/75b1387d72e2847220441166c5eb4e9846dd753895208c13e6d66523b2d9/sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85", size = 2154148 }, + { url = "https://files.pythonhosted.org/packages/d8/a4/7805e02323c49cb9d1ae5cd4913b28c97103079765f520043f914fca4cb3/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4", size = 3233051 }, + { url = "https://files.pythonhosted.org/packages/d7/ec/32ae09139f61bef3de3142e85c47abdee8db9a55af2bb438da54a4549263/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0", size = 3232781 }, + { url = "https://files.pythonhosted.org/packages/ad/bd/bf7b869b6f5585eac34222e1cf4405f4ba8c3b85dd6b1af5d4ce8bca695f/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0", size = 3182096 }, + { url = "https://files.pythonhosted.org/packages/21/6a/c219720a241bb8f35c88815ccc27761f5af7fdef04b987b0e8a2c1a6dcaa/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826", size = 3205109 }, + { url = "https://files.pythonhosted.org/packages/bd/c4/6ccf31b2bc925d5d95fab403ffd50d20d7c82b858cf1a4855664ca054dce/sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a", size = 2114240 }, + { url = "https://files.pythonhosted.org/packages/de/29/a27a31fca07316def418db6f7c70ab14010506616a2decef1906050a0587/sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7", size = 2137615 }, + { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672 }, ] [[package]] @@ -3867,6 +4147,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, ] +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138 }, +] + [[package]] name = "stack-data" version = "0.6.3" @@ -3896,7 +4185,7 @@ wheels = [ [[package]] name = "streamlit" -version = "1.21.0" +version = "1.52.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "altair" }, @@ -3904,7 +4193,6 @@ dependencies = [ { name = "cachetools" }, { name = "click" }, { name = "gitpython" }, - { name = "importlib-metadata" }, { name = "numpy" }, { name = "packaging" }, { name = "pandas" }, @@ -3912,20 +4200,16 @@ dependencies = [ { name = "protobuf" }, { name = "pyarrow" }, { name = "pydeck" }, - { name = "pympler" }, - { name = "python-dateutil" }, { name = "requests" }, - { name = "rich" }, + { name = "tenacity" }, { name = "toml" }, { name = "tornado" }, { name = "typing-extensions" }, - { name = "tzlocal" }, - { name = "validators" }, { name = "watchdog", marker = "platform_system != 'Darwin'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/95/89b0f5f51cd006ffb6cc216393793d05029c47e97f140a9bff02f8c291a8/streamlit-1.21.0.tar.gz", hash = "sha256:c72d9639508679c5e411d1f886d213777759501d01975285c049dc30db463c1a", size = 9345809 } +sdist = { url = "https://files.pythonhosted.org/packages/43/20/434aaceccc6e1912671d869926103051330437adba72d538d787a07727ef/streamlit-1.52.2.tar.gz", hash = "sha256:64a4dda8bc5cdd37bfd490e93bb53da35aaef946fcfc283a7980dacdf165108b", size = 8584178 } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/fa/5b010610aa136c44a20de6ed9ec0b36867daf7322d0e3c8e626a99fab11a/streamlit-1.21.0-py2.py3-none-any.whl", hash = "sha256:05862598952a9126e16652caa5bc88eeeea7b2773af252e2daccf1c84ceaaef4", size = 9665093 }, + { url = "https://files.pythonhosted.org/packages/c0/95/6b7873f0267973ebd55ba9cd33a690b35a116f2779901ef6185a0e21864d/streamlit-1.52.2-py3-none-any.whl", hash = "sha256:a16bb4fbc9781e173ce9dfbd8ffb189c174f148f9ca4fb8fa56423e84e193fc8", size = 9025937 }, ] [[package]] @@ -3984,25 +4268,25 @@ wheels = [ [[package]] name = "tensorboardx" -version = "2.6" +version = "2.6.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "packaging" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/e3/c68897ee471518b1f8d3e53c602aec6f2b09092aa774da88764262f03f56/tensorboardX-2.6.tar.gz", hash = "sha256:d4c036964dd2deb075a1909832b276daa383eab3f9db519ad90b99f5aea06b0c", size = 91196 } +sdist = { url = "https://files.pythonhosted.org/packages/2b/c5/d4cc6e293fb837aaf9f76dd7745476aeba8ef7ef5146c3b3f9ee375fe7a5/tensorboardx-2.6.4.tar.gz", hash = "sha256:b163ccb7798b31100b9f5fa4d6bc22dad362d7065c2f24b51e50731adde86828", size = 4769801 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/9f/d532d37f10ac7af136d4c2ba71e1fe7af0f3cc0cc076dfc05826171e9737/tensorboardX-2.6-py2.py3-none-any.whl", hash = "sha256:24a7cd076488de1e9d15ef25371b8ebf90c4f8f622af2477c611198f03f4a606", size = 114480 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/b5d63f1a6b824282b57f7b581810d20b7a28ca951f2d5b59f1eb0782c12b/tensorboardx-2.6.4-py3-none-any.whl", hash = "sha256:5970cf3a1f0a6a6e8b180ccf46f3fe832b8a25a70b86e5a237048a7c0beb18e2", size = 87201 }, ] [[package]] name = "termcolor" -version = "3.2.0" +version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/56/ab275c2b56a5e2342568838f0d5e3e66a32354adcc159b495e374cda43f5/termcolor-3.2.0.tar.gz", hash = "sha256:610e6456feec42c4bcd28934a8c87a06c3fa28b01561d46aa09a9881b8622c58", size = 14423 } +sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl", hash = "sha256:a10343879eba4da819353c55cb8049b0933890c2ebf9ad5d3ecd2bb32ea96ea6", size = 7698 }, + { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734 }, ] [[package]] @@ -4061,27 +4345,32 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.22.1" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318 }, - { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478 }, - { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994 }, - { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141 }, - { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049 }, - { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730 }, - { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560 }, - { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221 }, - { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569 }, - { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599 }, - { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862 }, - { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250 }, - { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003 }, - { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684 }, +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301 }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308 }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964 }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542 }, ] [[package]] @@ -4102,15 +4391,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408 }, ] -[[package]] -name = "toolz" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, -] - [[package]] name = "torch" version = "2.9.1" @@ -4162,21 +4442,21 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.2" +version = "6.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821 } +sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563 }, - { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729 }, - { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295 }, - { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644 }, - { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878 }, - { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549 }, - { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973 }, - { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954 }, - { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023 }, - { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427 }, - { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456 }, + { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909 }, + { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163 }, + { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746 }, + { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083 }, + { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315 }, + { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003 }, + { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412 }, + { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392 }, + { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481 }, + { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886 }, + { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910 }, ] [[package]] @@ -4255,7 +4535,7 @@ wheels = [ [[package]] name = "typer" -version = "0.20.0" +version = "0.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -4263,9 +4543,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492 } +sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381 }, ] [[package]] @@ -4291,23 +4571,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, -] - -[[package]] -name = "tzlocal" -version = "5.3.1" +version = "2025.3" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tzdata", marker = "platform_system == 'Windows'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026 }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521 }, ] [[package]] @@ -4330,25 +4598,25 @@ wheels = [ [[package]] name = "urllib3" -version = "2.6.0" +version = "2.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/43/554c2569b62f49350597348fc3ac70f786e3c32e7f19d266e19817812dd3/urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1", size = 432585 } +sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/1a/9ffe814d317c5224166b23e7c47f606d6e473712a2fad0f704ea9b99f246/urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f", size = 131083 }, + { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182 }, ] [[package]] name = "uvicorn" -version = "0.38.0" +version = "0.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109 }, + { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502 }, ] [package.optional-dependencies] @@ -4376,15 +4644,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261 }, ] -[[package]] -name = "validators" -version = "0.35.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712 }, -] - [[package]] name = "virtualenv" version = "20.35.4" @@ -4400,6 +4659,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095 }, ] +[[package]] +name = "waitress" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232 }, +] + [[package]] name = "watchdog" version = "6.0.0" @@ -4507,6 +4775,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, ] +[[package]] +name = "werkzeug" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/ea/b0f8eeb287f8df9066e56e831c7824ac6bab645dd6c7a8f4b2d767944f9b/werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e", size = 864687 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/f9/9e082990c2585c744734f85bec79b5dae5df9c974ffee58fe421652c8e91/werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905", size = 224960 }, +] + [[package]] name = "widgetsnbextension" version = "4.0.15" From d349c693f89e3378cddc1dbca2d1eccbc6770b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:21:10 -0300 Subject: [PATCH 030/101] Feature/document extract config (#69) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Enhance document extraction with caching and configuration options * ✅ Update extractor tests to handle additional configuration parameters and improve error handling * 🔧 Update marker model warmup to include configuration setup for improved initialization * 🔖 Update aymurai package version to 2.0.0a6.dev3 * ⏪ Revert multiprocessing context change in run_safe_text_extraction * 🔖 Update aymurai package version to 2.0.0a6.dev5 * 🔥 Remove unused multiprocessing import from document_extract.py * 🔥 Remove unused logging import from extraction.py * 🔧 Change default value of force_ocr to False in pdf_to_text function * 📝 Update argument descriptions in pdf_to_text and plain_text_extractor functions to include default values * 📝 Remove duplicate argument description for path in BaseExtractor.extract method --- .../routers/misc/document_extract.py | 75 ++++++++++-- aymurai/api/startup/marker.py | 9 +- aymurai/text/extraction.py | 5 +- aymurai/text/extractors/base.py | 3 +- aymurai/text/extractors/docx.py | 3 +- aymurai/text/extractors/odt.py | 6 +- aymurai/text/extractors/pdf.py | 56 ++++++++- aymurai/text/extractors/utils.py | 109 +++++++++++++++--- test/text/test_extraction.py | 6 +- test/text/test_extractors.py | 55 +++++++++ uv.lock | 2 +- 11 files changed, 284 insertions(+), 45 deletions(-) diff --git a/aymurai/api/endpoints/routers/misc/document_extract.py b/aymurai/api/endpoints/routers/misc/document_extract.py index bdbcfc2e..7322b07a 100644 --- a/aymurai/api/endpoints/routers/misc/document_extract.py +++ b/aymurai/api/endpoints/routers/misc/document_extract.py @@ -19,16 +19,33 @@ router = APIRouter() -def extraction(path: str) -> str: +def extraction( + path: str, + use_cache: bool = True, + **kwargs, +) -> str: """ Wrapper function to call the extract_document function. This is necessary to ensure that the function can be pickled and run in a separate process. + + Args: + path (str): Path to the file to be processed. + use_cache (bool): Whether to use caching for the extraction. + **kwargs: Extractor-specific configuration overrides. + + Returns: + str: Extracted text from the document. """ - text = extract_document(path) + text = extract_document(path, use_cache=use_cache, **kwargs) return document_normalize(text) if text else "" -def run_safe_text_extraction(path: str, timeout_s: float | None = None) -> str: +def run_safe_text_extraction( + path: str, + timeout_s: float | None = None, + use_cache: bool = True, + **kwargs, +) -> str: """ Runs the text extraction in a separate process to avoid blocking the main thread. This is useful for long-running tasks or when the extraction might hang. @@ -37,6 +54,8 @@ def run_safe_text_extraction(path: str, timeout_s: float | None = None) -> str: path (str): Path to the file to be processed. timeout_s (float | None): Timeout in seconds for the extraction process. If None, waits indefinitely. Defaults to None. + use_cache (bool): Whether to use caching for the extraction. + **kwargs: Extractor-specific configuration overrides. Returns: str: Extracted text from the document. @@ -44,9 +63,8 @@ def run_safe_text_extraction(path: str, timeout_s: float | None = None) -> str: Raises: TimeoutError: If the extraction process exceeds the specified timeout. """ - with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor: - future = executor.submit(extraction, path) + future = executor.submit(extraction, path, use_cache, **kwargs) try: return future.result(timeout=timeout_s) except concurrent.futures.TimeoutError: @@ -56,7 +74,38 @@ def run_safe_text_extraction(path: str, timeout_s: float | None = None) -> str: @router.post("/document-extract", response_model=Document) -def plain_text_extractor(file: UploadFile) -> Document: +def plain_text_extractor( + file: UploadFile, + use_cache: bool = True, + layout_batch_size: int = 8, + detection_batch_size: int = 8, + table_rec_batch_size: int = 8, + recognition_batch_size: int = 8, + ocr_error_batch_size: int = 8, + force_ocr: bool = False, + strip_existing_ocr: bool = True, + torch_device: str | None = None, + debug: bool | None = None, +) -> Document: + """ + Extract plain text from an uploaded document. + + Args: + file (UploadFile): Incoming document upload. + use_cache (bool): Whether to use caching for the extraction. Defaults to True. + layout_batch_size (int): Batch size for layout model inference. Defaults to 8. + detection_batch_size (int): Batch size for detection model inference. Defaults to 8. + table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. + recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. + ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. + force_ocr (bool): Force OCR even if text is detected. Defaults to False. + strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. + torch_device (str | None): Optional override for the torch device. Defaults to None. + debug (bool | None): Optional override for marker debug mode. Defaults to None. + + Returns: + Document: Extracted and normalized document payload. + """ logger.info(f"receiving => {file.filename}") extension = MIMETYPE_EXTENSION_MAPPER.get(file.content_type) logger.info(f"detected extension: {extension} ({file.content_type})") @@ -74,7 +123,19 @@ def plain_text_extractor(file: UploadFile) -> Document: logger.info(f"saved temp file on local storage => {tmp_filename}") - document = run_safe_text_extraction(tmp_filename) + document = run_safe_text_extraction( + tmp_filename, + use_cache=use_cache, + layout_batch_size=layout_batch_size, + detection_batch_size=detection_batch_size, + table_rec_batch_size=table_rec_batch_size, + recognition_batch_size=recognition_batch_size, + ocr_error_batch_size=ocr_error_batch_size, + force_ocr=force_ocr, + strip_existing_ocr=strip_existing_ocr, + torch_device=torch_device, + debug=debug, + ) except concurrent.futures.TimeoutError: logger.error(f"Timeout while extracting text from {file.filename}") diff --git a/aymurai/api/startup/marker.py b/aymurai/api/startup/marker.py index 208afa43..fa40aa48 100644 --- a/aymurai/api/startup/marker.py +++ b/aymurai/api/startup/marker.py @@ -1,5 +1,9 @@ from aymurai.logger import get_logger -from aymurai.text.extractors.utils import get_marker_pdf_converter_and_md_renderer +from aymurai.text.extractors.utils import ( + _build_marker_pdf_config, + _marker_config_key, + get_marker_pdf_converter_and_md_renderer, +) logger = get_logger(__name__) @@ -7,7 +11,8 @@ def warm_marker_models() -> None: """Download marker-pdf artifacts at startup to avoid first-request latency.""" try: - get_marker_pdf_converter_and_md_renderer() + config = _build_marker_pdf_config() + get_marker_pdf_converter_and_md_renderer(_marker_config_key(config)) logger.info("marker-pdf models are ready") except Exception as exc: logger.warning("marker-pdf warmup failed: %s", exc) diff --git a/aymurai/text/extraction.py b/aymurai/text/extraction.py index 1e2735d2..b57cb531 100644 --- a/aymurai/text/extraction.py +++ b/aymurai/text/extraction.py @@ -1,4 +1,3 @@ -import logging import mimetypes import os import zipfile @@ -83,6 +82,7 @@ def get_extension(path: str) -> str: def extract_document( filename: str | Path, errors: str = "ignore", + use_cache: bool = True, **kwargs, ) -> str | None: """ @@ -96,6 +96,7 @@ def extract_document( and warn. - If :const:`'ignore'`, then invalid parsing will be set as :const:`NaN` but not warn. + use_cache (bool, optional): Toggle extractor-level caching. Defaults to True. **kwargs: keyword arguments for textract. Raises: @@ -127,7 +128,7 @@ def extract_document( extractor = get_extractor(ext) try: - return extractor.extract(Path(filename)) + return extractor.extract(Path(filename), use_cache=use_cache, **kwargs) except InvalidFile as exc: if errors == "raise": raise diff --git a/aymurai/text/extractors/base.py b/aymurai/text/extractors/base.py index 20282b09..1603519d 100644 --- a/aymurai/text/extractors/base.py +++ b/aymurai/text/extractors/base.py @@ -34,12 +34,13 @@ def ensure_file(self, path: Path) -> Path: return path @abstractmethod - def extract(self, path: Path) -> str: + def extract(self, path: Path, **kwargs) -> str: """ Extract normalized text from the source document. Args: path (Path): Input document path. + **kwargs: Optional extractor-specific flags. Returns: str: Cleaned textual content. diff --git a/aymurai/text/extractors/docx.py b/aymurai/text/extractors/docx.py index ae698e20..52824449 100644 --- a/aymurai/text/extractors/docx.py +++ b/aymurai/text/extractors/docx.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Any from zipfile import BadZipFile import docx2txt @@ -11,7 +12,7 @@ class DocxExtractor(BaseExtractor): extension = "docx" - def extract(self, path: Path) -> str: + def extract(self, path: Path, **_: Any) -> str: file_path = self.ensure_file(path) try: diff --git a/aymurai/text/extractors/odt.py b/aymurai/text/extractors/odt.py index 7aaf3caa..41a55355 100644 --- a/aymurai/text/extractors/odt.py +++ b/aymurai/text/extractors/odt.py @@ -1,7 +1,7 @@ from pathlib import Path -from zipfile import BadZipFile - +from typing import Any from xml.etree.ElementTree import ParseError +from zipfile import BadZipFile from aymurai.text.extractors.base import BaseExtractor, InvalidFile, register_extractor from aymurai.text.extractors.utils import get_header, normalize_text, odt_to_text @@ -11,7 +11,7 @@ class OdtExtractor(BaseExtractor): extension = "odt" - def extract(self, path: Path) -> str: + def extract(self, path: Path, **_: Any) -> str: file_path = self.ensure_file(path) try: diff --git a/aymurai/text/extractors/pdf.py b/aymurai/text/extractors/pdf.py index 7a7e41b3..c4b273c2 100644 --- a/aymurai/text/extractors/pdf.py +++ b/aymurai/text/extractors/pdf.py @@ -13,24 +13,68 @@ class PdfExtractor(BaseExtractor): extension = "pdf" - def extract(self, path: Path) -> str: + def extract( + self, + path: Path, + *, + use_cache: bool = True, + layout_batch_size: int = 8, + detection_batch_size: int = 8, + table_rec_batch_size: int = 8, + recognition_batch_size: int = 8, + ocr_error_batch_size: int = 8, + force_ocr: bool = False, + strip_existing_ocr: bool = True, + torch_device: str | None = None, + debug: bool | None = None, + ) -> str: + """ + Extract normalized text from a PDF document. + + Args: + path (Path): Input document path. + use_cache (bool): Toggle extractor-level caching. Defaults to True. + layout_batch_size (int): Batch size for layout model inference. Defaults to 8. + detection_batch_size (int): Batch size for detection model inference. Defaults to 8. + table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. + recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. + ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. + force_ocr (bool): Force OCR even if text is detected. Defaults to False. + strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. + torch_device (str | None): Optional override for the torch device. Defaults to None. + debug (bool | None): Optional override for marker debug mode. Defaults to None. + + Returns: + str: Cleaned textual content. + """ file_path = self.ensure_file(path) - # Check cache first - cache_key = self._cache_key(file_path) - if cache_key: + # Check cache first when enabled + cache_key = self._cache_key(file_path) if use_cache else None + if use_cache and cache_key: cached_text = cache_load(cache_key) if cached_text is not None: logger.debug("PDF cache hit for %s", file_path) return cached_text try: - text = pdf_to_text(file_path) + text = pdf_to_text( + file_path, + layout_batch_size=layout_batch_size, + detection_batch_size=detection_batch_size, + table_rec_batch_size=table_rec_batch_size, + recognition_batch_size=recognition_batch_size, + ocr_error_batch_size=ocr_error_batch_size, + force_ocr=force_ocr, + strip_existing_ocr=strip_existing_ocr, + torch_device=torch_device, + debug=debug, + ) except (OSError, ValueError) as exc: raise InvalidFile(str(exc)) from exc except Exception as exc: raise InvalidFile(str(exc)) from exc - if cache_key: + if use_cache and cache_key: cache_save(text, key=cache_key) logger.debug("PDF cache stored for %s", file_path) diff --git a/aymurai/text/extractors/utils.py b/aymurai/text/extractors/utils.py index b7c9520b..6a98ba30 100644 --- a/aymurai/text/extractors/utils.py +++ b/aymurai/text/extractors/utils.py @@ -23,15 +23,7 @@ BLOCK_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "blockquote", "pre"} -MARKER_PDF_CONFIG = { - "layout_batch_size": 8, - "detection_batch_size": 8, - "table_rec_batch_size": 8, - "recognition_batch_size": 8, - "ocr_error_batch_size": 8, - "force_ocr": True, - "strip_existing_ocr": True, -} +MarkerPdfConfig = dict[str, int | str | bool] ODT_NS = {"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0"} @@ -71,37 +63,76 @@ def markdown_to_text(md: str) -> str: return "\n\n".join(filter(None, chunks)) -def _build_marker_pdf_config() -> dict[str, int | str | bool]: +def _build_marker_pdf_config( + layout_batch_size: int = 8, + detection_batch_size: int = 8, + table_rec_batch_size: int = 8, + recognition_batch_size: int = 8, + ocr_error_batch_size: int = 8, + force_ocr: bool = False, + strip_existing_ocr: bool = True, + torch_device: str | None = None, + debug: bool | None = None, +) -> MarkerPdfConfig: """ Build marker configuration factoring in environment overrides. + Args: + layout_batch_size (int): Batch size for layout model inference. Defaults to 8. + detection_batch_size (int): Batch size for detection model inference. Defaults to 8. + table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. + recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. + ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. + force_ocr (bool): Force OCR even if text is detected. Defaults to False. + strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. + torch_device (str | None): Optional override for the torch device. Defaults to None. + debug (bool | None): Optional override for marker debug mode. Defaults to None. + Returns: dict[str, int | str | bool]: Effective configuration for marker-pdf. """ - config = MARKER_PDF_CONFIG.copy() - - torch_device = os.getenv("TORCH_DEVICE") + config: MarkerPdfConfig = { + "layout_batch_size": layout_batch_size, + "detection_batch_size": detection_batch_size, + "table_rec_batch_size": table_rec_batch_size, + "recognition_batch_size": recognition_batch_size, + "ocr_error_batch_size": ocr_error_batch_size, + "force_ocr": force_ocr, + "strip_existing_ocr": strip_existing_ocr, + } + + if torch_device is None: + torch_device = os.getenv("TORCH_DEVICE") if torch_device: config["TORCH_DEVICE"] = torch_device - log_level = os.getenv("LOG_LEVEL", "").lower() - if log_level == "debug": + if debug is None: + log_level = os.getenv("LOG_LEVEL", "").lower() + debug = log_level == "debug" + + if debug: config["debug"] = True return config @cache -def get_marker_pdf_converter_and_md_renderer() -> tuple[PdfConverter, MarkdownRenderer]: +def get_marker_pdf_converter_and_md_renderer( + config_items: tuple[tuple[str, int | str | bool], ...], +) -> tuple[PdfConverter, MarkdownRenderer]: """ Provide cached marker PDF converter and Markdown renderer instances. + Args: + config_items (tuple[tuple[str, int | str | bool], ...]): Sorted config items + to build a stable cache key. + Returns: tuple[PdfConverter, MarkdownRenderer]: Ready-to-use converter and renderer. """ pdf_converter = PdfConverter( artifact_dict=create_model_dict(), - config=_build_marker_pdf_config(), + config=dict(config_items), ) markdown_renderer = MarkdownRenderer( @@ -114,18 +145,58 @@ def get_marker_pdf_converter_and_md_renderer() -> tuple[PdfConverter, MarkdownRe return pdf_converter, markdown_renderer -def pdf_to_text(file_path: Path) -> str: +def _marker_config_key( + config: MarkerPdfConfig, +) -> tuple[tuple[str, int | str | bool], ...]: + return tuple(sorted(config.items())) + + +def pdf_to_text( + file_path: Path, + *, + layout_batch_size: int = 8, + detection_batch_size: int = 8, + table_rec_batch_size: int = 8, + recognition_batch_size: int = 8, + ocr_error_batch_size: int = 8, + force_ocr: bool = False, + strip_existing_ocr: bool = True, + torch_device: str | None = None, + debug: bool | None = None, +) -> str: """ Extract text from a PDF file and return normalized plain text. Args: file_path (Path): Path to the PDF document. + layout_batch_size (int): Batch size for layout model inference. Defaults to 8. + detection_batch_size (int): Batch size for detection model inference. Defaults to 8. + table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. + recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. + ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. + force_ocr (bool): Force OCR even if text is detected. Defaults to False. + strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. + torch_device (str | None): Optional override for the torch device. Defaults to None. + debug (bool | None): Optional override for marker debug mode. Defaults to None. Returns: str: Cleaned textual content extracted from the PDF. """ logger.info("Extracting text from PDF: %s", file_path) - pdf_converter, markdown_renderer = get_marker_pdf_converter_and_md_renderer() + config = _build_marker_pdf_config( + layout_batch_size=layout_batch_size, + detection_batch_size=detection_batch_size, + table_rec_batch_size=table_rec_batch_size, + recognition_batch_size=recognition_batch_size, + ocr_error_batch_size=ocr_error_batch_size, + force_ocr=force_ocr, + strip_existing_ocr=strip_existing_ocr, + torch_device=torch_device, + debug=debug, + ) + pdf_converter, markdown_renderer = get_marker_pdf_converter_and_md_renderer( + _marker_config_key(config) + ) document = pdf_converter.build_document(filepath=file_path.as_posix()) markdown_output = markdown_renderer(document) plain_text = markdown_to_text(markdown_output.markdown) diff --git a/test/text/test_extraction.py b/test/text/test_extraction.py index 5e4dba56..69df8f7e 100644 --- a/test/text/test_extraction.py +++ b/test/text/test_extraction.py @@ -21,7 +21,7 @@ def __init__(self) -> None: self.called = False self.called_path: Path | None = None - def extract(self, path: Path) -> str: + def extract(self, path: Path, **_: object) -> str: self.called = True self.called_path = path return "ok" @@ -48,7 +48,7 @@ def test_extract_document_handles_invalid_file(self): source.write_text("dummy") class BoomExtractor: - def extract(self, _path: Path) -> str: + def extract(self, _path: Path, **_: object) -> str: raise InvalidFile("boom") with patch( @@ -64,7 +64,7 @@ def test_extract_document_raises_unexpected(self): source.write_text("dummy") class BoomExtractor: - def extract(self, _path: Path) -> str: + def extract(self, _path: Path, **_: object) -> str: raise RuntimeError("boom") with patch( diff --git a/test/text/test_extractors.py b/test/text/test_extractors.py index 8beed5ff..37c883d8 100644 --- a/test/text/test_extractors.py +++ b/test/text/test_extractors.py @@ -92,6 +92,42 @@ def test_pdf_extractor_delegates(self): self.assertEqual(result, "PDF text") + def test_pdf_extractor_passes_config(self): + file_path = self.tmp_path / "sample.pdf" + file_path.write_bytes(b"fake pdf") + + with patch( + "aymurai.text.extractors.pdf.pdf_to_text", + return_value="PDF text", + ) as pdf_to_text: + extractor = PdfExtractor() + extractor.extract( + file_path, + use_cache=False, + layout_batch_size=4, + detection_batch_size=5, + table_rec_batch_size=6, + recognition_batch_size=7, + ocr_error_batch_size=8, + force_ocr=False, + strip_existing_ocr=False, + torch_device="cpu", + debug=True, + ) + + pdf_to_text.assert_called_once_with( + file_path, + layout_batch_size=4, + detection_batch_size=5, + table_rec_batch_size=6, + recognition_batch_size=7, + ocr_error_batch_size=8, + force_ocr=False, + strip_existing_ocr=False, + torch_device="cpu", + debug=True, + ) + def test_pdf_extractor_wraps_errors(self): file_path = self.tmp_path / "broken.pdf" file_path.write_bytes(b"") @@ -104,6 +140,25 @@ def test_pdf_extractor_wraps_errors(self): with self.assertRaises(InvalidFile): extractor.extract(file_path) + def test_odt_extractor_ignores_extra_kwargs(self): + file_path = self.tmp_path / "sample.odt" + file_path.write_bytes(b"fake odt content") + + with ( + patch( + "aymurai.text.extractors.odt.odt_to_text", + return_value="Paragraph one", + ), + patch( + "aymurai.text.extractors.odt.get_header", + return_value=[], + ), + ): + extractor = OdtExtractor() + result = extractor.extract(file_path, layout_batch_size=4) + + self.assertIn("Paragraph one", result) + if __name__ == "__main__": unittest.main() diff --git a/uv.lock b/uv.lock index dabb9fdc..be85f765 100644 --- a/uv.lock +++ b/uv.lock @@ -284,7 +284,7 @@ wheels = [ [[package]] name = "aymurai" -version = "2.0.0a5.dev8" +version = "2.0.0a6.dev5" source = { editable = "." } dependencies = [ { name = "alembic" }, From b6829afe64b420a0996f664a35c06f81ae4f82e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:45:39 -0300 Subject: [PATCH 031/101] Feature/pre disambiguation optimization (#70) * New pre-disambigutation feature notebooks * New pre-disambigutation feature notebooks and metrics.py per label feature added * Conclusion added to pre-cluster investigation * utils.py ocr variable True * Changes in grid search function to store the best pre-clusterizated entities in a particular directory * New llm inference function in notebook 07 * New llm grid search inference function * Add disambiguation endpoint and utility functions for entity grouping * Remove unused models and tokenizers to streamline the codebase * Fix type hints for processor functions to avoid runtime errors --- .devcontainer/devcontainer.json | 4 +- .../routers/anonymizer/anonymizer.py | 64 +- .../api/endpoints/routers/anonymizer/utils.py | 212 ++ aymurai/evaluation/metrics.py | 62 +- ...tion-pre-clustered-manual-validation.ipynb | 2447 +++++++++++++++++ .../06-pre-disambiguation-optimization.ipynb | 1619 +++++++++++ .../07-PER-entities-disambiguation.ipynb | 1487 ++++++++++ .../08-disambiguation-endpoint-smoke.ipynb | 185 ++ uv.lock | 1953 +++++++------ 9 files changed, 7028 insertions(+), 1005 deletions(-) create mode 100644 aymurai/api/endpoints/routers/anonymizer/utils.py create mode 100644 notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb create mode 100644 notebooks/experiments/entity-disambiguation/06-pre-disambiguation-optimization.ipynb create mode 100644 notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb create mode 100644 notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5aa72ad5..d5eeaafa 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,9 +4,9 @@ "name": "aymurai", // "initializeCommand": "make core-build", "dockerComposeFile": "docker-compose.yml", - "service": "aymurai-devcontainer-gpu", + "service": "aymurai-devcontainer", "runServices": [ - "aymurai-devcontainer-gpu" + "aymurai-devcontainer" ], "workspaceFolder": "/workspace", "mounts": [ diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 00a562dd..63025082 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -5,12 +5,18 @@ from threading import Lock import torch -from fastapi import Body, Depends, Form, Query, UploadFile +from fastapi import Body, Depends, Form, HTTPException, Query, UploadFile from fastapi.responses import FileResponse from fastapi.routing import APIRouter from sqlmodel import Session from starlette.background import BackgroundTask +from aymurai.api.endpoints.routers.anonymizer.utils import ( + PROCESSOR_MAP, + SCORER_MAP, + build_canonical_entities, + resolve_processor, +) from aymurai.api.utils import load_pipeline from aymurai.database.crud.anonymization.document import anonymization_document_create from aymurai.database.crud.anonymization.paragraph import ( @@ -28,6 +34,7 @@ DocumentInformation, TextRequest, ) +from aymurai.meta.entities import CanonicalEntities from aymurai.settings import settings from aymurai.text.anonymization import DocAnonymizer from aymurai.text.extraction import MIMETYPE_EXTENSION_MAPPER @@ -107,6 +114,61 @@ async def anonymizer_paragraph_predict( return DocumentInformation(document=text, labels=paragraph.prediction) +@router.post("/disambiguate", response_model=CanonicalEntities) +async def anonymizer_disambiguate( + paragraphs: list[DocumentInformation] = Body( + ..., + description=( + "List of per-paragraph predictions returned by /anonymizer/predict." + ), + ), + target_labels: list[str] + | None = Query( + None, + description="Optional label filter, e.g. PER,DNI.", + ), + threshold: int = Query( + 70, + description="Minimum similarity score (0-100) to cluster entities.", + ), + scorer: str = Query( + "token_set_ratio", + description="RapidFuzz scorer name for similarity.", + ), + processor: str = Query( + "light_normalizer", + description="Text processor to normalize before similarity.", + ), +) -> CanonicalEntities: + """ + Prototype endpoint for canonical entity grouping using fuzzy matching. + """ + if threshold < 0 or threshold > 100: + raise HTTPException(status_code=400, detail="threshold must be 0-100.") + + scorer_fn = SCORER_MAP.get(scorer.lower()) + if scorer_fn is None: + raise HTTPException(status_code=400, detail=f"Unsupported scorer: {scorer}") + + if processor.lower() not in PROCESSOR_MAP: + raise HTTPException( + status_code=400, detail=f"Unsupported processor: {processor}" + ) + processor_fn = resolve_processor(processor) + + labels = [label for paragraph in paragraphs for label in (paragraph.labels or [])] + + target_set = {label.strip() for label in target_labels} if target_labels else None + canonical_entities = build_canonical_entities( + labels, + target_labels=target_set, + threshold=threshold, + scorer=scorer_fn, + processor=processor_fn, + ) + return CanonicalEntities(canonical_entities=canonical_entities) + + # MARK: Validate @router.post("/validation", response_model=list[DocLabel] | None) async def anonymizer_get_paragraph_validation( diff --git a/aymurai/api/endpoints/routers/anonymizer/utils.py b/aymurai/api/endpoints/routers/anonymizer/utils.py new file mode 100644 index 00000000..9c25cea2 --- /dev/null +++ b/aymurai/api/endpoints/routers/anonymizer/utils.py @@ -0,0 +1,212 @@ +import re +import unicodedata +from collections import Counter +from typing import Callable, Iterable + +from rapidfuzz import process +from rapidfuzz.fuzz import ( + WRatio, + partial_ratio, + partial_token_set_ratio, + partial_token_sort_ratio, + ratio, + token_set_ratio, + token_sort_ratio, +) + +from aymurai.meta.api_interfaces import DocLabel +from aymurai.meta.entities import CanonicalEntity + +__all__ = [ + "SCORER_MAP", + "PROCESSOR_MAP", + "build_canonical_entities", + "resolve_processor", +] + +SCORER_MAP = { + "ratio": ratio, + "partial_ratio": partial_ratio, + "token_sort_ratio": token_sort_ratio, + "token_set_ratio": token_set_ratio, + "partial_token_set_ratio": partial_token_set_ratio, + "partial_token_sort_ratio": partial_token_sort_ratio, + "wratio": WRatio, +} + +PROCESSOR_MAP = { + "none": None, + "light_normalizer": "light_normalizer", + "hard_normalizer": "hard_normalizer", + "legal_text_normalizer": "legal_text_normalizer", +} + + +def hard_normalizer(text: str) -> str: + if not text: + return "" + + normalized = "".join( + char + for char in unicodedata.normalize("NFD", text) + if unicodedata.category(char) != "Mn" + ) + normalized = re.sub(r"[.,\-]", " ", normalized) + normalized = " ".join(normalized.lower().split()) + return normalized + + +def light_normalizer(text: str) -> str: + return text.lower().strip() if text else "" + + +def legal_text_normalizer(text: str) -> str: + if not text: + return "" + + normalized = "".join( + char + for char in unicodedata.normalize("NFD", text) + if unicodedata.category(char) != "Mn" + ) + normalized = normalized.lower() + + legal_titles = r"\b(dr|dra|sr|sra|expte|nro|no|pcia)\b\.?" + normalized = re.sub(legal_titles, "", normalized) + + stopwords = r"\b(de|del|la|las|el|los|y|en)\b" + normalized = re.sub(stopwords, "", normalized) + normalized = re.sub(r"[^\w\s]", "", normalized) + + return " ".join(normalized.split()) + + +def resolve_processor(name: str) -> Callable[[str], str] | None: + key = name.lower() + mapped = PROCESSOR_MAP.get(key) + if mapped is None: + return None + if mapped == "light_normalizer": + return light_normalizer + if mapped == "hard_normalizer": + return hard_normalizer + if mapped == "legal_text_normalizer": + return legal_text_normalizer + return None + + +def cluster_with_cdist( + *, + items: list[dict[str, str]], + threshold: int, + scorer: Callable[[str, str], float], + processor: Callable[[str], str] | None, +) -> list[list[tuple[str, str, str]]]: + if not items: + return [] + + entities = [item.get("text", "") for item in items] + labels = [item.get("aymurai_label", "UNKNOWN") for item in items] + + if processor: + normed = [processor(entity) for entity in entities] + else: + normed = [str(entity) for entity in entities] + + sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold) + + parent = list(range(len(normed))) + + def find(idx: int) -> int: + if parent[idx] == idx: + return idx + parent[idx] = find(parent[idx]) + return parent[idx] + + def union(left: int, right: int) -> None: + root_left, root_right = find(left), find(right) + if root_left != root_right: + parent[root_right] = root_left + + n = len(normed) + for i in range(n): + for j in range(i + 1, n): + if sim[i][j] >= threshold: + union(i, j) + + clusters_map: dict[int, list[tuple[str, str, str]]] = {} + for idx in range(n): + root = find(idx) + clusters_map.setdefault(root, []).append( + (entities[idx], normed[idx], labels[idx]) + ) + + return list(clusters_map.values()) + + +def parse_item(item: tuple[str, str, str]) -> tuple[str, str, str]: + orig, norm, label = item + return label, orig, norm + + +def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str: + labels = [label for label, _, _ in parsed_items] + return Counter(labels).most_common(1)[0][0] + + +def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str: + return max(parsed_items, key=lambda item: len(item[1]))[1] + + +def clusters_to_canonical_entities( + clusters: list[list[tuple[str, str, str]]], +) -> list[CanonicalEntity]: + canonical_entities = [] + for cluster in clusters: + parsed = [parse_item(item) for item in cluster] + label = pick_cluster_label(parsed) + canonical_text = pick_canonical_text(parsed) + aliases = sorted({orig for _, orig, _ in parsed}) + canonical_entities.append( + CanonicalEntity( + aymurai_label=label, + canonical_text=canonical_text, + aliases=aliases, + attributes={}, + relations=[], + ) + ) + return canonical_entities + + +def build_canonical_entities( + labels: Iterable[DocLabel], + *, + target_labels: set[str] | None = None, + threshold: int, + scorer: Callable[[str, str], float], + processor: Callable[[str], str] | None, +) -> list[CanonicalEntity]: + grouped: dict[str, list[dict[str, str]]] = {} + for label in labels: + attrs = label.attrs + if not attrs or not attrs.aymurai_label: + continue + if target_labels and attrs.aymurai_label not in target_labels: + continue + alias = attrs.aymurai_alt_text or label.text + grouped.setdefault(attrs.aymurai_label, []).append( + {"text": alias, "aymurai_label": attrs.aymurai_label} + ) + + canonical_entities = [] + for items in grouped.values(): + clusters = cluster_with_cdist( + items=items, + threshold=threshold, + scorer=scorer, + processor=processor, + ) + canonical_entities.extend(clusters_to_canonical_entities(clusters)) + + return canonical_entities diff --git a/aymurai/evaluation/metrics.py b/aymurai/evaluation/metrics.py index e9b2058a..bea0a785 100644 --- a/aymurai/evaluation/metrics.py +++ b/aymurai/evaluation/metrics.py @@ -142,9 +142,10 @@ def compute_metrics_components( pred_entities: list[dict[str, Any]], sim_threshold: float = 0.3, normalize: bool = True, + target_label: str = None, ) -> dict[str, float]: """ - Compute each component of the disambiguation metric. + Compute each component of the disambiguation metric by label if provided. The metric includes entity-level F1, macro average alias F1, and accuracy for labels and roles on matched entities. @@ -160,6 +161,14 @@ def compute_metrics_components( Returns: dict[str, float]: Mapping with the keys F1_ent, AliasF1_macro, Acc_label, and Acc_role. """ + # Filter entities by target label if provided + if target_label: + gold_entities = [ + e for e in gold_entities if e.get("aymurai_label") == target_label + ] + pred_entities = [ + e for e in pred_entities if e.get("aymurai_label") == target_label + ] # Handle the trivial case where both sets are empty if not gold_entities and not pred_entities: @@ -268,9 +277,10 @@ def evaluate_disambiguation( w_role: float = 0.05, sim_threshold: float = 0.3, normalize: bool = True, + target_label: str = None, ) -> tuple[float, dict[str, float]]: """ - Evaluate disambiguation predictions against ground truth entities. + Evaluate disambiguation predictions against ground truth entities by label if provided. Args: gold_json (Any): Ground-truth entities as a parsed list or JSON string. @@ -300,7 +310,11 @@ def evaluate_disambiguation( pred_entities = pred_json metrics = compute_metrics_components( - gold_entities, pred_entities, sim_threshold=sim_threshold, normalize=normalize + gold_entities, + pred_entities, + sim_threshold=sim_threshold, + normalize=normalize, + target_label=target_label, ) score = ( @@ -314,41 +328,60 @@ def evaluate_disambiguation( def evaluate_prediction_directories( - test_dir: Path, + gold_dir: Path, preds_dir: Path, *, sim_threshold: float = 0.3, normalize: bool = True, + target_label: str = None, + gold_json_suffix: str = None, + pred_json_suffix: str = None, ) -> tuple[list[tuple[str, float, dict[str, float]]], float]: """ - Evaluate predictions stored on disk against the gold standard set. + Evaluate predictions stored on disk against the gold standard set by label if provided. + It has the feature of comparing files in two directories based on their core names, + ignoring predefined suffixes. Args: - test_dir (Path): Directory containing gold json files. + gold_dir (Path): Directory containing gold json files. preds_dir (Path): Directory containing predicted json files. sim_threshold (float): Minimum alias similarity to consider entities a match. Defaults to 0.3. normalize (bool): If True normalizes aliases before comparison. Defaults to True. + target_label (str, optional): If provided, evaluates only entities with this label. + Defaults to None. + gold_json_suffix (str, optional): Suffix to remove from gold json filenames + when matching with predictions. Defaults to None. + pred_json_suffix (str, optional): Suffix to remove from predicted json filenames + when matching with gold files. Defaults to None. Returns: tuple[list[tuple[str, float, dict[str, float]]], float]: Detailed results per document and the average score across all evaluated documents. """ - test_dir = Path(test_dir) + gold_dir = Path(gold_dir) preds_dir = Path(preds_dir) results = [] - for test_path in sorted(test_dir.glob("*.json")): - doc_id = test_id_from_filename(test_path) - pred_path = preds_dir / prediction_filename_for_test(test_path) + pred_map = {} + for p in preds_dir.glob("*.json"): + core_name = p.stem.replace(pred_json_suffix, "") + pred_map[core_name] = p - if not pred_path.exists(): - logger.warning(f"Prediction for {test_path.name} was not found; skipping.") + for gold_path in sorted(gold_dir.glob("*.json")): + core_id = gold_path.stem.replace(gold_json_suffix, "") + pred_path = pred_map.get(core_id) + + if not pred_path or not pred_path.exists(): + logger.warning( + f"Skipping: No prediction found for '{core_id}'. " + f"Expected something like '{core_id}{pred_json_suffix}.json'" + ) continue - gold_data = load_json(test_path) + gold_data = load_json(gold_path) pred_data = load_json(pred_path) score, metrics = evaluate_disambiguation( @@ -356,8 +389,9 @@ def evaluate_prediction_directories( pred_data, sim_threshold=sim_threshold, normalize=normalize, + target_label=target_label, ) - results.append((doc_id, score, metrics)) + results.append((core_id, score, metrics)) average_score = ( sum(score for _, score, _ in results) / len(results) diff --git a/notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb b/notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb new file mode 100644 index 00000000..f34b64d0 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb @@ -0,0 +1,2447 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c8fc128c", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8eeefe8b", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import mimetypes\n", + "import os\n", + "import re\n", + "import time\n", + "import unicodedata\n", + "from collections import Counter\n", + "from operator import itemgetter\n", + "from pathlib import Path\n", + "from typing import Iterable\n", + "\n", + "import requests\n", + "from tqdm import tqdm\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import requests\n", + "from more_itertools import flatten, unique_everseen\n", + "from rapidfuzz import fuzz, process\n", + "\n", + "from aymurai.llm_providers import OllamaLLMProvider\n", + "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", + "from aymurai.utils.json_data import get_pretty, save_json, load_json" + ] + }, + { + "cell_type": "markdown", + "id": "84d11bba", + "metadata": {}, + "source": [ + "## /document-extract endpoint output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e0ec450", + "metadata": {}, + "outputs": [], + "source": [ + "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://127.0.0.1:8000\")\n", + "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", + "DATA_ROOT = Path(\n", + " os.getenv(\"DOCUMENT_DATA_ROOT\", \"../../../resources/data/restricted/disambiguation-eval/files\")\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef352767", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "print(f\"Discovered {len(documents)} documents.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21fd6e36", + "metadata": {}, + "outputs": [], + "source": [ + "def call_extraction_api(\n", + " session: requests.Session, file_path: Path\n", + ") -> dict[str, object]:\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " ENDPOINT,\n", + " files=files,\n", + " timeout=REQUEST_TIMEOUT_S,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload" + ] + }, + { + "cell_type": "markdown", + "id": "9254ef68", + "metadata": {}, + "source": [ + "## Inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a308cf90", + "metadata": {}, + "outputs": [], + "source": [ + "# Function to make inference using the API\n", + "def get_predictions(sample: str) -> dict:\n", + " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample})\n", + " response.raise_for_status()\n", + " return response.json()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ef6a0fa", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import chain\n", + "from operator import itemgetter\n", + "from typing import Any\n", + "\n", + "from more_itertools import unique_everseen\n", + "\n", + "\n", + "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", + " \"\"\"\n", + " Parse prediction labels to extract unique aymurai_label and aymurai_alt_text pairs.\n", + "\n", + " Args:\n", + " predictions (list[dict[str, Any]]): A list of prediction dictionaries.\n", + "\n", + " Returns:\n", + " list[dict[str, str]]: A list of dictionaries containing unique aymurai_label and aymurai_alt_text pairs.\n", + " \"\"\"\n", + " attrs_stream = (\n", + " label.get(\"attrs\") or {}\n", + " for label in chain.from_iterable(pred.get(\"labels\", ()) for pred in predictions)\n", + " )\n", + "\n", + " unique_pairs = unique_everseen(\n", + " (\n", + " attrs.get(\"aymurai_label\"),\n", + " attrs.get(\"aymurai_alt_text\"),\n", + " )\n", + " for attrs in attrs_stream\n", + " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", + " )\n", + "\n", + " return sorted(\n", + " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", + " key=itemgetter(\"aymurai_label\", \"text\"),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "52d6b14d", + "metadata": {}, + "source": [ + "# 8241_90_17_10_2025_128 CAUSA 14663_2020-2" + ] + }, + { + "cell_type": "markdown", + "id": "7e113ea4", + "metadata": {}, + "source": [ + "In this case, I took the .json file that Juli passed me and made a first manual correction of the clustering he did in the [notebook 04 pipeline](04-entity-disambiguation-from-pre-clustered-validations.ipynb).\n", + "\n", + "For this .json, a function needs to be created to ensure that, instead of grouping the files into a single canonical entity, it divides each one into a different canonical entity of the type ***NOMBRE_ARCHIVO***." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa6bbadf", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare target filename\n", + "target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(documents[1]))[0]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "934be599", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare json to review\n", + "json_path = \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + "target_path = json_path + target_filename + \"-canonical-entities.json\"\n", + "canonical_entities_to_review = load_json(target_path)\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities_to_review = [\n", + " CanonicalEntity.model_validate(entity) for entity in canonical_entities_to_review\n", + "]\n", + "canonical_entities_to_review = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities_to_review\n", + "]\n", + "\n", + "canonical_entities_to_review" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b94f9ec8", + "metadata": {}, + "outputs": [], + "source": [ + "from copy import deepcopy\n", + "\n", + "def split_aliases_by_label(items: list[CanonicalEntity], target_label: str) -> list[CanonicalEntity]:\n", + " \"\"\"\n", + " Splits aliases into new CanonicalEntities if the item matches target_label.\n", + " Removes non-matching aliases from the original entity.\n", + " \"\"\"\n", + " processed_items = []\n", + "\n", + " for item in items:\n", + " # pass through items that don't match the target label\n", + " if item['aymurai_label'] != target_label:\n", + " processed_items.append(item)\n", + " continue\n", + "\n", + " # if it matches, we process the aliases\n", + " kept_aliases = []\n", + " new_entities = []\n", + "\n", + " for alias in item['aliases']:\n", + " if alias == item['canonical_text']:\n", + " # Keep this alias in the original item\n", + " kept_aliases.append(alias)\n", + " else:\n", + " # Create a NEW entity for this alias\n", + " new_ce = CanonicalEntity(\n", + " aymurai_label=target_label,\n", + " canonical_text=alias, # The alias becomes the new canonical text\n", + " aliases=[alias], # New entity starts with no aliases\n", + " attributes=deepcopy(item['attributes']),\n", + " relations=deepcopy(item['relations'])\n", + " )\n", + " new_entities.append(new_ce)\n", + "\n", + " # update the original item's aliases\n", + " item['aliases'] = kept_aliases\n", + " \n", + " # add the original item to the result\n", + " processed_items.append(item)\n", + " \n", + " # add the newly created entities to the result\n", + " processed_items.extend(new_entities)\n", + "\n", + " return processed_items" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d86ee5bb", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities_to_review = split_aliases_by_label(canonical_entities_to_review, target_label='NOMBRE_ARCHIVO')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b21bc5f4", + "metadata": {}, + "outputs": [], + "source": [ + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in canonical_entities_to_review\n", + "]\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b9218a8", + "metadata": {}, + "outputs": [], + "source": [ + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7e362c6", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute this cell to load reviewed canonical entities\n", + "reviewed_canonical_entities = load_json(\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", + "]\n", + "\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d138d674", + "metadata": {}, + "outputs": [], + "source": [ + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "973600c7", + "metadata": {}, + "source": [ + "## CanonicalEntity extraction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16a8520a", + "metadata": {}, + "outputs": [], + "source": [ + "# Available models\n", + "model = \"phi4:14b\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63c6d798", + "metadata": {}, + "outputs": [], + "source": [ + "# Sanity check\n", + "provider = OllamaLLMProvider(model=model)\n", + "provider.generate(\"hola, ¿cómo estás?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e665c5a5", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " stream_prompt = \"hola, ¿cómo estás?\"\n", + " pieces = []\n", + " for event in provider.stream(stream_prompt):\n", + " print(event.text, end=\"\", flush=True)\n", + " pieces.append(event.text)\n", + " print()\n", + " full_text = \"\".join(pieces)\n", + "\n", + "except Exception as exc:\n", + " print(\"La transmisión no está disponible:\", exc)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80706810", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = \"\"\"\n", + "Eres un asistente especializado en anonimización de sentencias judiciales.\n", + "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", + "Una **entidad canónica** es la representación única de una entidad real.\n", + "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", + "\n", + "# Reglas\n", + "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", + "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", + "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", + "- Cada entidad canónica debe incluir:\n", + " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", + " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", + " - `aliases` (todas las menciones textuales relevantes).\n", + " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", + "\n", + "# Notas\n", + "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", + "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + "\n", + "# Ejemplo\n", + "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", + "A: ```json\n", + "[\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Laura Beatriz Gómez\",\n", + " \"aliases\": [\"Laura Beatriz Gómez\"],\n", + " \"attributes\": {\"role\": \"Denunciante\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"42987654\",\n", + " \"aliases\": [\"42.987.654\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DIRECCION\",\n", + " \"canonical_text\": \"calle Falsa 123\",\n", + " \"aliases\": [\"calle Falsa 123\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", + " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", + " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", + " \"attributes\": {\"role\": \"Denunciado\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"27654321\",\n", + " \"aliases\": [\"27.654.321\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"calle Real 789\",\n", + " \"aliases\": [\"calle Real 789\"]\n", + " }\n", + "]\n", + "```\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "055fa7a6", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template = \"\"\"\n", + "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", + "\n", + "# Documento\n", + "{document_text}\n", + "\n", + "# Menciones de entidades canónicas agrupadas\n", + "{canonical_entities}\n", + "\n", + "# Instrucciones\n", + "1. Evalúa cada entidad canónica listada, poniendo especial atención en las aliases.\n", + "2. Si alguna alias no corresponde a esa entidad, elimínala de la lista de aliases y créala como una nueva entidad canónica.\n", + "3. Las diferencias textuales correctas en los aliases pueden ser variantes ortográficas, abreviaciones, iniciales o errores tipográficos.\n", + "4. Diferencias sutiles que permiten desambiguar entidades distintas (por ejemplo, nombres similares de personas diferentes, fechas próximas pero distintas, números de documentos, códigos de identificación o nombres de archivos con pequeñas variaciones, etc.).\n", + "5. Normaliza canonical_text; mantén los aliases tal como aparecen.\n", + "6. Identifica roles u otros atributos relevantes en el campo attributes (por ejemplo, {{\"role\": \"Denunciante\"}}).\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9bc94b25", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "session = requests.Session()\n", + "document = call_extraction_api(session, Path(documents[1]))\n", + "document = document.get(\"detail\", {}).get(\"document\")\n", + "\n", + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d53eae0", + "metadata": {}, + "outputs": [], + "source": [ + "# Get paragraphs with at least one NER predicted label\n", + "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + "pred_paragraphs = [paragraph['document'] for paragraph in ner_predictions if paragraph['labels']]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2b8a84d", + "metadata": {}, + "outputs": [], + "source": [ + "# Remove empty 'entity_id', 'relations' and 'attributes' fields\n", + "canonical_entities = [\n", + " {\n", + " k: v\n", + " for k, v in ce.items()\n", + " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", + " }\n", + " for ce in canonical_entities\n", + "]\n", + "\n", + "# Prepare user prompt\n", + "user_prompt = user_prompt_template.format(\n", + " document_text=\"\\n\".join(pred_paragraphs).strip(),\n", + " canonical_entities=get_pretty(canonical_entities),\n", + ")\n", + "\n", + "print(user_prompt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04e638df", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " stream_prompt = messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ]\n", + " pieces = []\n", + " for event in provider.stream(\n", + " messages=stream_prompt,\n", + " options={\"temperature\": 0, \"num_ctx\": 9_500},\n", + " format=CanonicalEntities.model_json_schema(),\n", + " ):\n", + " print(event.text, end=\"\", flush=True)\n", + " pieces.append(event.text)\n", + " print()\n", + " full_text = \"\".join(pieces)\n", + "\n", + "except Exception as exc:\n", + " print(\"La transmisión no está disponible:\", exc)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1377bcfa", + "metadata": {}, + "outputs": [], + "source": [ + "# Get canonical entities from the model\n", + "\n", + "provider = OllamaLLMProvider(model=model)\n", + "response = provider.generate(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options={\"temperature\": 0, \"num_ctx\": 16_384},\n", + " format=CanonicalEntities.model_json_schema(),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2868141f", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = [\n", + " output for output in json.loads(response.text)[\"canonical_entities\"]\n", + " ]\n", + "\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(canonical_entity)\n", + " for canonical_entity in canonical_entities\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0337fe0c", + "metadata": {}, + "outputs": [], + "source": [ + "# Save LLM-predicted canonical entities\n", + "os.makedirs(\"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\", exist_ok=True) \n", + "\n", + "target_path = Path(\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "save_json(\n", + " [\n", + " canonical_entity.model_dump()\n", + " | {\"entity_id\": canonical_entity.entity_id.hex}\n", + " for canonical_entity in canonical_entities\n", + " ],\n", + " str(target_path),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1ff322aa", + "metadata": {}, + "source": [ + "# 01 - NN x 5C" + ] + }, + { + "cell_type": "markdown", + "id": "924a5fc2", + "metadata": {}, + "source": [ + "# Pipeline NER - Clusterization - Validation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2197430d", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "session = requests.Session()\n", + "document = call_extraction_api(session, Path(documents[0]))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e8b1e99", + "metadata": {}, + "outputs": [], + "source": [ + "document = document.get(\"detail\", {}).get(\"document\")\n", + "\n", + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")\n", + "\n", + "# Get NER predictions\n", + "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", + "ner_output_json = get_pretty(parsed_ner_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74407f30", + "metadata": {}, + "outputs": [], + "source": [ + "def normalize(s: str) -> str:\n", + " \"\"\"\n", + " Normalize string for clustering.\n", + "\n", + " Args:\n", + " s (str): input string\n", + "\n", + " Returns:\n", + " str: normalized string\n", + " \"\"\"\n", + " # strip accents, lowercase, collapse spaces\n", + " s = \"\".join(\n", + " c for c in unicodedata.normalize(\"NFD\", s) if unicodedata.category(c) != \"Mn\"\n", + " )\n", + " s = \" \".join(s.lower().split())\n", + " return s\n", + "\n", + "\n", + "def cluster_with_cdist(\n", + " entities: list[str], threshold: int = 90, scorer: callable = fuzz.token_set_ratio\n", + "):\n", + " \"\"\"\n", + " Cluster entities based on similarity using a distance matrix.\n", + "\n", + " Args:\n", + " entities (list[str]): list of entity strings\n", + " threshold (int): similarity threshold for clustering\n", + " scorer (callable): similarity scoring function\n", + "\n", + " Returns:\n", + " list[list[tuple[str, str]]]: clusters of (original, normalized) entity tuples\n", + " \"\"\"\n", + " # matrix of similarities on normalized strings\n", + " # normalize however you like; here just lower + strip\n", + " normed = [\" \".join(e.lower().split()) for e in entities]\n", + " sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold)\n", + " sim = np.array(sim)\n", + "\n", + " # union-find\n", + " parent = list(range(len(normed)))\n", + "\n", + " def find(i):\n", + " while parent[i] != i:\n", + " parent[i] = parent[parent[i]]\n", + " i = parent[i]\n", + " return i\n", + "\n", + " def union(i, j):\n", + " ri, rj = find(i), find(j)\n", + " if ri != rj:\n", + " parent[rj] = ri\n", + "\n", + " # link pairs above threshold (upper triangle only)\n", + " n = len(normed)\n", + " for i in range(n):\n", + " for j in range(i + 1, n):\n", + " if sim[i, j] >= threshold:\n", + " union(i, j)\n", + "\n", + " # collect clusters\n", + " clusters = {}\n", + " for idx in range(n):\n", + " root = find(idx)\n", + " clusters.setdefault(root, []).append((entities[idx], normed[idx]))\n", + " return list(clusters.values())\n", + "\n", + "\n", + "def pick_canonical(cluster: list[tuple[str, str]]) -> str:\n", + " \"\"\"\n", + " Pick canonical text from a cluster.\n", + "\n", + " Args:\n", + " cluster (list[tuple[str, str]]): cluster of (original, normalized) entity tuples\n", + "\n", + " Returns:\n", + " str: chosen canonical text\n", + " \"\"\"\n", + " # prefer longest original; fall back to first\n", + " return max(cluster, key=lambda x: len(x[0]))[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a355dc9", + "metadata": {}, + "outputs": [], + "source": [ + "# Cluster entities\n", + "clusters = cluster_with_cdist(\n", + " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", + " threshold=95,\n", + ")\n", + "\n", + "for cluster in clusters:\n", + " canonical = pick_canonical(cluster)\n", + " originals = [c[0] for c in cluster]\n", + " print(f\"Canonical: {canonical} -> {originals}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1f5b54c", + "metadata": {}, + "outputs": [], + "source": [ + "def parse_item(item: tuple[str, ...]) -> tuple[str, str, str]:\n", + " \"\"\"\n", + " Parse an item into (label, orig, norm).\n", + "\n", + " Accepts:\n", + " - (orig, norm, label)\n", + " - (labelled_orig, labelled_norm) with prefix 'LABEL:'\n", + " Args:\n", + " item (tuple[str, ...]): input item\n", + "\n", + " Returns:\n", + " tuple[str, str, str]: (label, orig, norm)\n", + " \"\"\"\n", + " if len(item) == 3:\n", + " orig, norm, label = item\n", + " return label, orig, norm\n", + "\n", + " # len == 2: assume \"LABEL:text\"\n", + " labelled_orig, labelled_norm = item\n", + " label, orig = labelled_orig.split(\":\", 1)\n", + " _, norm = labelled_norm.split(\":\", 1)\n", + " return label, orig, norm\n", + "\n", + "\n", + "def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Pick the most common label from parsed items.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen label\n", + " \"\"\"\n", + " labels = [lbl for lbl, _, _ in parsed_items]\n", + " # majority vote; fallback to first\n", + " return Counter(labels).most_common(1)[0][0]\n", + "\n", + "\n", + "def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Choose the longest original surface form; tweak as needed.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen canonical text\n", + " \"\"\"\n", + " return max(parsed_items, key=lambda x: len(x[1]))[1]\n", + "\n", + "\n", + "def clusters_to_canonical_entities(\n", + " clusters: list[list[tuple[str, str]]],\n", + ") -> list[CanonicalEntity]:\n", + " \"\"\"\n", + " Convert clusters to CanonicalEntity objects.\n", + "\n", + " Args:\n", + " clusters (list[list[tuple[str, str]]]): clusters of (original, normalized) entity tuples\n", + "\n", + " Returns:\n", + " list[CanonicalEntity]: list of CanonicalEntity objects\n", + " \"\"\"\n", + " canonical_entities = []\n", + "\n", + " for cluster in clusters:\n", + " parsed = [parse_item(item) for item in cluster] # [(label, orig, norm), ...]\n", + " label = pick_cluster_label(parsed)\n", + " canonical_text = pick_canonical_text(parsed)\n", + " aliases = sorted({orig for _, orig, _ in parsed})\n", + " ce = CanonicalEntity(\n", + " aymurai_label=label,\n", + " canonical_text=canonical_text,\n", + " aliases=aliases,\n", + " attributes={},\n", + " relations=[],\n", + " )\n", + " canonical_entities.append(ce)\n", + "\n", + " return canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "758cf776", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = clusters_to_canonical_entities(clusters)\n", + "\n", + "# Sort by label and canonical text\n", + "canonical_entities = sorted(\n", + " canonical_entities,\n", + " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", + ")\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ba35eb5", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare target filename\n", + "target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(documents[0]))[0]\n", + ")\n", + "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19f0af6f", + "metadata": {}, + "outputs": [], + "source": [ + "target_filename" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bab1a112", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Save canonical entities to JSON in to-review folder to save the raw output\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save canonical entities to JSON in reviewing folder to make te review manually\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "8cc94d9c", + "metadata": {}, + "source": [ + "Make a pause and review the json manually for those wrong outputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17c09855", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute this cell to load reviewed canonical entities\n", + "reviewed_canonical_entities = load_json(\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", + "]\n", + "\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64ac9530", + "metadata": {}, + "outputs": [], + "source": [ + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "40a2dced", + "metadata": {}, + "source": [ + "## CanonicalEntity extraction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1e15dce", + "metadata": {}, + "outputs": [], + "source": [ + "# Available models\n", + "model = \"phi4:14b\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "edba631d", + "metadata": {}, + "outputs": [], + "source": [ + "# Sanity check\n", + "provider = OllamaLLMProvider(model=model)\n", + "provider.generate(\"hola, ¿cómo estás?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cdb2d734", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = \"\"\"\n", + "Eres un asistente especializado en anonimización de sentencias judiciales.\n", + "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", + "Una **entidad canónica** es la representación única de una entidad real.\n", + "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", + "\n", + "# Reglas\n", + "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", + "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", + "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", + "- Cada entidad canónica debe incluir:\n", + " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", + " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", + " - `aliases` (todas las menciones textuales relevantes).\n", + " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", + "\n", + "# Notas\n", + "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", + "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + "\n", + "# Ejemplo\n", + "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", + "A: ```json\n", + "[\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Laura Beatriz Gómez\",\n", + " \"aliases\": [\"Laura Beatriz Gómez\"],\n", + " \"attributes\": {\"role\": \"Denunciante\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"42987654\",\n", + " \"aliases\": [\"42.987.654\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DIRECCION\",\n", + " \"canonical_text\": \"calle Falsa 123\",\n", + " \"aliases\": [\"calle Falsa 123\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", + " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", + " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", + " \"attributes\": {\"role\": \"Denunciado\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"27654321\",\n", + " \"aliases\": [\"27.654.321\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"calle Real 789\",\n", + " \"aliases\": [\"calle Real 789\"]\n", + " }\n", + "]\n", + "```\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bedf1a83", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template = \"\"\"\n", + "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", + "\n", + "# Documento\n", + "{document_text}\n", + "\n", + "# Menciones de entidades canónicas agrupadas\n", + "{canonical_entities}\n", + "\n", + "# Instrucciones\n", + "1. Evalúa cada entidad canónica listada, poniendo especial atención en las aliases.\n", + "2. Si alguna alias no corresponde a esa entidad, elimínala de la lista de aliases y créala como una nueva entidad canónica.\n", + "3. Las diferencias textuales correctas en los aliases pueden ser variantes ortográficas, abreviaciones, iniciales o errores tipográficos.\n", + "4. Diferencias sutiles que permiten desambiguar entidades distintas (por ejemplo, nombres similares de personas diferentes, fechas próximas pero distintas, números de documentos, códigos de identificación o nombres de archivos con pequeñas variaciones, etc.).\n", + "5. Normaliza canonical_text; mantén los aliases tal como aparecen.\n", + "6. Identifica roles u otros atributos relevantes en el campo attributes (por ejemplo, {{\"role\": \"Denunciante\"}}).\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be7ced82", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "session = requests.Session()\n", + "document = call_extraction_api(session, Path(documents[0]))\n", + "document = document.get(\"detail\", {}).get(\"document\")\n", + "\n", + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc9607a8", + "metadata": {}, + "outputs": [], + "source": [ + "# Get paragraphs with at least one NER predicted label\n", + "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + "pred_paragraphs = [paragraph['document'] for paragraph in ner_predictions if paragraph['labels']]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42a89610", + "metadata": {}, + "outputs": [], + "source": [ + "# Remove empty 'entity_id', 'relations' and 'attributes' fields\n", + "canonical_entities = [\n", + " {\n", + " k: v\n", + " for k, v in ce.items()\n", + " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", + " }\n", + " for ce in canonical_entities\n", + "]\n", + "\n", + "# Prepare user prompt\n", + "user_prompt = user_prompt_template.format(\n", + " document_text=\"\\n\".join(document).strip(),\n", + " canonical_entities=get_pretty(canonical_entities),\n", + ")\n", + "\n", + "print(user_prompt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6d3e2bc9", + "metadata": {}, + "outputs": [], + "source": [ + "# Get canonical entities from the model\n", + "provider = OllamaLLMProvider(model=model)\n", + "response = provider.generate(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options={\"temperature\": 0, \"num_ctx\": 9_500},\n", + " format=CanonicalEntities.model_json_schema(),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "172ed8d4", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = [\n", + " output for output in json.loads(response.text)[\"canonical_entities\"]\n", + " ]\n", + "\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(canonical_entity)\n", + " for canonical_entity in canonical_entities\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87be578d", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24833143", + "metadata": {}, + "outputs": [], + "source": [ + "# Save LLM-predicted canonical entities\n", + "os.makedirs(\"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\", exist_ok=True) \n", + "\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "9e0a7186", + "metadata": {}, + "source": [ + "# 1- FLORES (pdf)" + ] + }, + { + "cell_type": "markdown", + "id": "f758c28c", + "metadata": {}, + "source": [ + "# Pipeline NER - Clusterization - Validation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f59197b", + "metadata": {}, + "outputs": [], + "source": [ + "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/1- FLORES ABARCA, Francisco Alexander s 149 bis J-01-00369422-0-2022-1 Sala Feria (II) nnya vict do apela pp_response_1765835782205.json\"\n", + "\n", + "json_path = Path(json_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d1e5f7b", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "document = load_json(json_path)[\"document\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb8086f8", + "metadata": {}, + "outputs": [], + "source": [ + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")\n", + "\n", + "# Get NER predictions\n", + "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", + "ner_output_json = get_pretty(parsed_ner_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "afde9c03", + "metadata": {}, + "outputs": [], + "source": [ + "parsed_ner_labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc66c703", + "metadata": {}, + "outputs": [], + "source": [ + "# Cluster entities\n", + "clusters = cluster_with_cdist(\n", + " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", + " threshold=95,\n", + ")\n", + "\n", + "for cluster in clusters:\n", + " canonical = pick_canonical(cluster)\n", + " originals = [c[0] for c in cluster]\n", + " print(f\"Canonical: {canonical} -> {originals}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6d06d5b", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = clusters_to_canonical_entities(clusters)\n", + "\n", + "# Sort by label and canonical text\n", + "canonical_entities = sorted(\n", + " canonical_entities,\n", + " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", + ")\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c62763a", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare target filename\n", + "target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", + ")\n", + "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3317ee7f", + "metadata": {}, + "outputs": [], + "source": [ + "# Save canonical entities to JSON in to-review folder to save the raw output\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save canonical entities to JSON in reviewing folder to make te review manually\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4add5e66", + "metadata": {}, + "source": [ + "Make a pause and review the json manually for those wrong outputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b56adf8", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute this cell to load reviewed canonical entities\n", + "reviewed_canonical_entities = load_json(\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", + "]\n", + "\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b30019e1", + "metadata": {}, + "outputs": [], + "source": [ + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e5f587ef", + "metadata": {}, + "source": [ + "# 2 - FISSCHER (pdf)" + ] + }, + { + "cell_type": "markdown", + "id": "9d446c92", + "metadata": {}, + "source": [ + "# Pipeline NER - Clusterization - Validation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a45a578b", + "metadata": {}, + "outputs": [], + "source": [ + "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/2 - FISSCHER Alejandro Claudio s - 92_response_1765908855010.json\"\n", + "\n", + "json_path = Path(json_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37fea663", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "document = load_json(json_path)[\"document\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca4de17f", + "metadata": {}, + "outputs": [], + "source": [ + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")\n", + "\n", + "# Get NER predictions\n", + "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", + "ner_output_json = get_pretty(parsed_ner_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d8b9c0f", + "metadata": {}, + "outputs": [], + "source": [ + "parsed_ner_labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa189443", + "metadata": {}, + "outputs": [], + "source": [ + "# Cluster entities\n", + "clusters = cluster_with_cdist(\n", + " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", + " threshold=95,\n", + ")\n", + "\n", + "for cluster in clusters:\n", + " canonical = pick_canonical(cluster)\n", + " originals = [c[0] for c in cluster]\n", + " print(f\"Canonical: {canonical} -> {originals}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d76f9a89", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = clusters_to_canonical_entities(clusters)\n", + "\n", + "# Sort by label and canonical text\n", + "canonical_entities = sorted(\n", + " canonical_entities,\n", + " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", + ")\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e65d30f", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare target filename\n", + "target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", + ")\n", + "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f42c8398", + "metadata": {}, + "outputs": [], + "source": [ + "# Save canonical entities to JSON in to-review folder to save the raw output\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save canonical entities to JSON in reviewing folder to make te review manually\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "2daee78a", + "metadata": {}, + "source": [ + "Make a pause and review the json manually for those wrong outputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5a1c1c9", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute this cell to load reviewed canonical entities\n", + "reviewed_canonical_entities = load_json(\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", + "]\n", + "\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28359165", + "metadata": {}, + "outputs": [], + "source": [ + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "dfc64748", + "metadata": {}, + "source": [ + "# 2 - Feb. 2 (pdf)" + ] + }, + { + "cell_type": "markdown", + "id": "edf32104", + "metadata": {}, + "source": [ + "# Pipeline NER - Clusterization - Validation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bbafb4e9", + "metadata": {}, + "outputs": [], + "source": [ + "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/2 - Feb. 2 - DE ON, Kwan x 13944_response_1765836659235.json\"\n", + "\n", + "json_path = Path(json_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "679a5817", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "document = load_json(json_path)[\"document\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f640f548", + "metadata": {}, + "outputs": [], + "source": [ + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")\n", + "\n", + "# Get NER predictions\n", + "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", + "ner_output_json = get_pretty(parsed_ner_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ce9cc2c1", + "metadata": {}, + "outputs": [], + "source": [ + "parsed_ner_labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a007f3c", + "metadata": {}, + "outputs": [], + "source": [ + "# Cluster entities\n", + "clusters = cluster_with_cdist(\n", + " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", + " threshold=95,\n", + ")\n", + "\n", + "for cluster in clusters:\n", + " canonical = pick_canonical(cluster)\n", + " originals = [c[0] for c in cluster]\n", + " print(f\"Canonical: {canonical} -> {originals}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5ef7c49", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = clusters_to_canonical_entities(clusters)\n", + "\n", + "# Sort by label and canonical text\n", + "canonical_entities = sorted(\n", + " canonical_entities,\n", + " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", + ")\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b461fdab", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare target filename\n", + "target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", + ")\n", + "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "564fc2a4", + "metadata": {}, + "outputs": [], + "source": [ + "# Save canonical entities to JSON in to-review folder to save the raw output\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save canonical entities to JSON in reviewing folder to make te review manually\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "f8329a42", + "metadata": {}, + "source": [ + "Make a pause and review the json manually for those wrong outputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ac09ef3", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute this cell to load reviewed canonical entities\n", + "reviewed_canonical_entities = load_json(\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", + "]\n", + "\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9006d7ec", + "metadata": {}, + "outputs": [], + "source": [ + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "2f7a2ba5", + "metadata": {}, + "source": [ + "# Jul. 18 (pdf)" + ] + }, + { + "cell_type": "markdown", + "id": "83b4b16e", + "metadata": {}, + "source": [ + "# Pipeline NER - Clusterization - Validation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "431ecd13", + "metadata": {}, + "outputs": [], + "source": [ + "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/Jul. 18- SALA IV- IPANAQUE VILLAR Carlos Hector s 5c nnapes_response_1765835513742.json\"\n", + "\n", + "json_path = Path(json_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "692fc5d5", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "document = load_json(json_path)[\"document\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4176067e", + "metadata": {}, + "outputs": [], + "source": [ + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")\n", + "\n", + "# Get NER predictions\n", + "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", + "ner_output_json = get_pretty(parsed_ner_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbf3644d", + "metadata": {}, + "outputs": [], + "source": [ + "parsed_ner_labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cfda586", + "metadata": {}, + "outputs": [], + "source": [ + "# Cluster entities\n", + "clusters = cluster_with_cdist(\n", + " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", + " threshold=95,\n", + ")\n", + "\n", + "for cluster in clusters:\n", + " canonical = pick_canonical(cluster)\n", + " originals = [c[0] for c in cluster]\n", + " print(f\"Canonical: {canonical} -> {originals}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0c6b557", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities = clusters_to_canonical_entities(clusters)\n", + "\n", + "# Sort by label and canonical text\n", + "canonical_entities = sorted(\n", + " canonical_entities,\n", + " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", + ")\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4443d7c9", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare target filename\n", + "target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", + ")\n", + "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9de334ef", + "metadata": {}, + "outputs": [], + "source": [ + "# Save canonical entities to JSON in to-review folder to save the raw output\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save canonical entities to JSON in reviewing folder to make te review manually\n", + "save_json(\n", + " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c0bb69fb", + "metadata": {}, + "source": [ + "Make a pause and review the json manually for those wrong outputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76b7ddc8", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute this cell to load reviewed canonical entities\n", + "reviewed_canonical_entities = load_json(\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\"\n", + ")\n", + "\n", + "# Convert reviewed entities back to CanonicalEntity objects\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", + "]\n", + "\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f5e15c9", + "metadata": {}, + "outputs": [], + "source": [ + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")\n", + "\n", + "# Save reviewed canonical entities\n", + "save_json(\n", + " canonical_entities,\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", + " + target_filename\n", + " + \"-canonical-entities.json\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "555c7c5f", + "metadata": {}, + "source": [ + "# Start point of the old version of the pipeline" + ] + }, + { + "cell_type": "markdown", + "id": "ccbe21f4", + "metadata": {}, + "source": [ + "## CanonicalEntity extraction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd025617", + "metadata": {}, + "outputs": [], + "source": [ + "# Available models\n", + "# model = \"gpt-oss:20b\"\n", + "# model = \"llama3\"\n", + "model = \"phi4:14b\"\n", + "# model = \"llama3.1:8b\"\n", + "# model = \"gemma3:270m\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25284997", + "metadata": {}, + "outputs": [], + "source": [ + "# Sanity check\n", + "provider = OllamaLLMProvider(model=model)\n", + "provider.generate(\"hola, ¿cómo estás?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33ca3dd3", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " stream_prompt = \"hola, ¿cómo estás?\"\n", + " pieces = []\n", + " for event in provider.stream(stream_prompt):\n", + " print(event.text, end=\"\", flush=True)\n", + " pieces.append(event.text)\n", + " print()\n", + " full_text = \"\".join(pieces)\n", + "\n", + "except Exception as exc:\n", + " print(\"La transmisión no está disponible:\", exc)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c7ec7c6", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = \"\"\"\n", + "Eres un asistente especializado en anonimización de sentencias judiciales.\n", + "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", + "Una **entidad canónica** es la representación única de una entidad real.\n", + "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", + "\n", + "# Reglas\n", + "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", + "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", + "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", + "- Cada entidad canónica debe incluir:\n", + " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", + " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", + " - `aliases` (todas las menciones textuales relevantes).\n", + " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", + "\n", + "# Notas\n", + "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", + "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + "\n", + "# Ejemplo\n", + "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", + "A: ```json\n", + "[\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Laura Beatriz Gómez\",\n", + " \"aliases\": [\"Laura Beatriz Gómez\"],\n", + " \"attributes\": {\"role\": \"Denunciante\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"42987654\",\n", + " \"aliases\": [\"42.987.654\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DIRECCION\",\n", + " \"canonical_text\": \"calle Falsa 123\",\n", + " \"aliases\": [\"calle Falsa 123\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", + " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", + " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", + " \"attributes\": {\"role\": \"Denunciado\"}\n", + " },\n", + " {\n", + " \"aymurai_label\": \"DNI\",\n", + " \"canonical_text\": \"27654321\",\n", + " \"aliases\": [\"27.654.321\"]\n", + " },\n", + " {\n", + " \"aymurai_label\": \"LOC\",\n", + " \"canonical_text\": \"calle Real 789\",\n", + " \"aliases\": [\"calle Real 789\"]\n", + " }\n", + "]\n", + "```\n", + "\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6994e3c3", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template = \"\"\"\n", + "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", + "\n", + "# Documento\n", + "{document_text}\n", + "\n", + "# Menciones de entidades detectadas por el NER\n", + "{ner_output_json}\n", + "\n", + "# Instrucciones\n", + "1. Evalúa cada mención listada. Si representa una entidad real, inclúyela en la lista de aliases de alguna entidad canónica.\n", + "2. No omitas entidades válidas, alias con iniciales ni variantes abreviadas.\n", + "3. Solo fusiona menciones cuando exista evidencia clara de que se refieren a la misma entidad.\n", + "4. Normaliza canonical_text; mantén los alias tal como aparecen.\n", + "5. Utiliza attributes para indicar roles (p. ej. {{\"role\": \"Denunciante\"}}) o aclaraciones de desambiguación.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "349fa489", + "metadata": {}, + "outputs": [], + "source": [ + "def extract_canonical_entities(\n", + " doc_path: str, model: str = \"phi4:14b\"\n", + ") -> CanonicalEntities:\n", + " \"\"\"\n", + " Extract canonical entities from a document.\n", + "\n", + " Args:\n", + " doc_path (str): The path to the document.\n", + " model (str, optional): The model to use for extraction. Defaults to \"phi4:14b\".\n", + "\n", + " Raises:\n", + " ValueError: If the document is empty or not found.\n", + " ValueError: If the document summary is empty or not found.\n", + "\n", + " Returns:\n", + " CanonicalEntities: The extracted canonical entities.\n", + " \"\"\"\n", + " # Extract document\n", + " session = requests.Session()\n", + " document = call_extraction_api(session, Path(doc_path))\n", + " document = document.get(\"detail\", {}).get(\"document\")\n", + "\n", + " if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")\n", + "\n", + " # Get NER predictions\n", + " ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", + " parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", + " ner_output_json = get_pretty(parsed_ner_labels)\n", + "\n", + " # Prepare user prompt\n", + " user_prompt = user_prompt_template.format(\n", + " document_text=\"\\n\".join(document).strip(),\n", + " ner_output_json=ner_output_json,\n", + " )\n", + "\n", + " # Get canonical entities from the model\n", + " provider = OllamaLLMProvider(model=model)\n", + " response = provider.generate(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options={\"temperature\": 0, \"num_ctx\": 12_000},\n", + " format=CanonicalEntities.model_json_schema(),\n", + " )\n", + "\n", + " # Parse canonical entities from response\n", + " canonical_entities = [\n", + " output for output in json.loads(response.text)[\"canonical_entities\"]\n", + " ]\n", + "\n", + " canonical_entities = [\n", + " CanonicalEntity.model_validate(canonical_entity)\n", + " for canonical_entity in canonical_entities\n", + " ]\n", + "\n", + " return canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52c14551", + "metadata": {}, + "outputs": [], + "source": [ + "output_root = Path(DATA_ROOT) / \"preds\"\n", + "output_root.mkdir(parents=True, exist_ok=True)\n", + "\n", + "prompt_version = \"1\"\n", + "\n", + "timestamp = time.strftime(\"%y%m%d_%H%M\")\n", + "base_dir_name = f\"preds-{model}-pv{prompt_version}-{timestamp}\"\n", + "output_dir = output_root / base_dir_name\n", + "\n", + "output_dir.mkdir(parents=True, exist_ok=False)\n", + "print(f\"Saving canonical entities to {output_dir}\")\n", + "\n", + "for doc_path in documents:\n", + " print(f\"Processing document: {doc_path}\")\n", + "\n", + " try:\n", + " canonical_entities = extract_canonical_entities(doc_path, model=model)\n", + " print(f\"Extracted {len(canonical_entities)} canonical entities.\")\n", + "\n", + " target_filename = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", + " )\n", + " target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)\n", + " target_path = output_dir / f\"{target_filename}.json\"\n", + "\n", + " save_json(\n", + " [\n", + " canonical_entity.model_dump()\n", + " | {\"entity_id\": canonical_entity.entity_id.hex}\n", + " for canonical_entity in canonical_entities\n", + " ],\n", + " str(target_path),\n", + " )\n", + "\n", + " except Exception as e:\n", + " print(f\"Error processing document {doc_path}: {e}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/entity-disambiguation/06-pre-disambiguation-optimization.ipynb b/notebooks/experiments/entity-disambiguation/06-pre-disambiguation-optimization.ipynb new file mode 100644 index 00000000..fa875add --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/06-pre-disambiguation-optimization.ipynb @@ -0,0 +1,1619 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c8f3d033", + "metadata": {}, + "source": [ + "# Entity Resolution & Hyperparameter Optimization\n", + "\n", + "This notebook outlines a systematic process for optimizing our **Entity Disambiguation** pipeline. The goal is to identify the most accurate configuration for grouping person mentions by comparing localized model outputs against their corresponding ground truth data.\n", + "\n", + "## Methodology\n", + "\n", + "### 1. Data Correspondence & Ground Truth\n", + "To ensure data integrity, we have moved away from a single global file approach. The pipeline now maintains a strict 1:1 correspondence between files to prevent entity overlaps across different documents. The evaluation process compares:\n", + "\n", + "* **`gold_json`**: The benchmark \"Gold Standard\" for a specific document.\n", + "* **`input_ner_preds_json`**: The raw NER model predictions for that specific input.\n", + "* **`cluster_json`**: The output generated by our clustering logic for those specific predictions.\n", + "\n", + "### 2. NER Extraction & Processing\n", + "We deploy our **Named Entity Recognition (NER)** model across the source documents. Due to performance constraints on macOS when processing large batches in a single loop, we process documents individually. \n", + "\n", + "This sequential processing allows us to:\n", + "1. Effectively utilize cache memory.\n", + "2. Identify and fix incomplete entities (e.g., missing `entity_id`) before the evaluation phase.\n", + "3. Ensure each `input_ner_preds_json` is correctly mapped to its `gold_json`.\n", + "\n", + "### 3. Clustering via `cluster_with_cdist`\n", + "The core of our pipeline is the `cluster_with_cdist` function, which transforms raw NER predictions into grouped clusters:\n", + "\n", + "* **Similarity Matrix**: Uses `process.cdist` from `rapidfuzz` to calculate an efficient similarity matrix based on a specific `scorer`, `score_cutoff threshold` and `processor`.\n", + "* **Union-Find Algorithm**: Implements **Disjoint Set Union (DSU)** logic to link entities that exceed the similarity threshold.\n", + "* **Canonical Selection**: Through `pick_canonical`, the system selects the most representative string for each group, prioritizing the longest original string.\n", + "\n", + "### 4. Systematic Grid Search & Evaluation\n", + "We execute a **Grid Search** by iterating through clustering hyperparameters (`Scorers`, `Thresholds`, and `Processors`). For each iteration, the resulting `cluster_json` is validated against its correspondent `gold_json`.\n", + "\n", + "#### Evaluation Metric: Weighted Disambiguation Score\n", + "The performance of each hyperparameter combination is measured by a composite score that balances four key dimensions:\n", + "\n", + "* **Entity-level F1**: Measures the overall quality of entity discovery.\n", + "* **Alias Macro F1**: Evaluates how well different name variants (aliases) are grouped.\n", + "* **Label Accuracy**: Ensures the primary name assigned to the cluster is correct.\n", + "* **Role Accuracy**: Validates the secondary attributes (roles) associated with the entity.\n", + "\n", + "#### Winner Selection\n", + "To ensure the robustness of our pipeline, the **winning combination** is determined by calculating the **best average score across all documents**. This cross-document averaging prevents overfitting to a single file and ensures that the selected `scorer`, `threshold` and `processor` perform consistently across the entire corpus.\n", + "\n", + "---\n", + "\n", + "> **Implementation Note:** By analyzing the weighted score across discrete file correspondences, we can determine the optimal configuration while avoiding the data pollution issues found in previous single-file versions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e14b29a", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0386121", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import mimetypes\n", + "import os\n", + "import re\n", + "import time\n", + "import unicodedata\n", + "from collections import Counter\n", + "from operator import itemgetter\n", + "from pathlib import Path\n", + "from typing import Iterable, Tuple\n", + "\n", + "import requests\n", + "from tqdm import tqdm\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import requests\n", + "from more_itertools import flatten, unique_everseen\n", + "\n", + "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", + "from aymurai.utils.json_data import get_pretty, save_json, load_json\n", + "import aymurai.evaluation.metrics as aymurai_metrics" + ] + }, + { + "cell_type": "markdown", + "id": "d4bd115d", + "metadata": {}, + "source": [ + "# #**1** First Step: Prepare the ***gold_json*** and ***ner_preds_json***" + ] + }, + { + "cell_type": "markdown", + "id": "34a4a0b7", + "metadata": {}, + "source": [ + "## /document-extract endpoint output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b76ddad4", + "metadata": {}, + "outputs": [], + "source": [ + "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://127.0.0.1:8999\")\n", + "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", + "DATA_ROOT = Path(\n", + " os.getenv(\n", + " \"DOCUMENT_DATA_ROOT\", \"../../../resources/data/restricted/disambiguation-eval/files\"\n", + " )\n", + ")\n", + "GOLD_JSON_ROOT = Path(\n", + " os.getenv(\n", + " \"GOLD_JSON_ROOT\",\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted\",\n", + " )\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "JSON_EXTENSION = {\".json\"}\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd44c127", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "print(f\"Discovered {len(documents)} documents.\")\n", + "\n", + "gold_jsons = discover_documents(GOLD_JSON_ROOT, JSON_EXTENSION)\n", + "print(f\"Discovered {len(gold_jsons)} gold JSON files.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efd22ec4", + "metadata": {}, + "outputs": [], + "source": [ + "def call_extraction_api(\n", + " session: requests.Session, file_path: Path\n", + ") -> dict[str, object]:\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " ENDPOINT,\n", + " files=files,\n", + " timeout=REQUEST_TIMEOUT_S,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload" + ] + }, + { + "cell_type": "markdown", + "id": "b0e88e1b", + "metadata": {}, + "source": [ + "## Inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11a316a1", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import chain\n", + "from operator import itemgetter\n", + "from typing import Any\n", + "\n", + "from more_itertools import unique_everseen\n", + "\n", + "\n", + "# Function to make inference using the API\n", + "def get_predictions(sample: str) -> dict:\n", + " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample})\n", + " response.raise_for_status()\n", + " return response.json()\n", + "\n", + "\n", + "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", + " \"\"\"\n", + " Parse prediction labels to extract unique aymurai_label and aymurai_alt_text pairs.\n", + "\n", + " Args:\n", + " predictions (list[dict[str, Any]]): A list of prediction dictionaries.\n", + "\n", + " Returns:\n", + " list[dict[str, str]]: A list of dictionaries containing unique aymurai_label and aymurai_alt_text pairs.\n", + " \"\"\"\n", + " attrs_stream = (\n", + " label.get(\"attrs\") or {}\n", + " for label in chain.from_iterable(pred.get(\"labels\", ()) for pred in predictions)\n", + " )\n", + "\n", + " unique_pairs = unique_everseen(\n", + " (\n", + " attrs.get(\"aymurai_label\"),\n", + " attrs.get(\"aymurai_alt_text\"),\n", + " )\n", + " for attrs in attrs_stream\n", + " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", + " )\n", + "\n", + " return sorted(\n", + " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", + " key=itemgetter(\"aymurai_label\", \"text\"),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "4a3a97df", + "metadata": {}, + "source": [ + "## ***gold_json*** and ***ner_preds_json*** for input to clusterization developement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b3b2a74", + "metadata": {}, + "outputs": [], + "source": [ + "def extract_and_save_entities(\n", + " document_paths: list[Path], output_path: Path, file_ending: str, target_label: str\n", + ") -> None:\n", + " \"\"\"\n", + " Processes a list of documents to extract specific entities and saves\n", + " each result as an individual JSON file.\n", + " It has been optimized to handle both document files and pre-existing JSON files.\n", + " \"\"\"\n", + "\n", + " with requests.Session() as session:\n", + " if \".json\" not in document_paths[0].suffix:\n", + " for doc_path in tqdm(\n", + " document_paths, desc=f\"Extracting {target_label} entities\"\n", + " ):\n", + " doc_path = Path(doc_path)\n", + "\n", + " # 1. API Extraction\n", + " response = call_extraction_api(session, doc_path)\n", + " document_data = response.get(\"detail\", {}).get(\"document\")\n", + "\n", + " if not document_data:\n", + " print(f\"Warning: No document content found for {doc_path.name}\")\n", + " continue\n", + "\n", + " # 2. Processing\n", + " raw_predictions = [\n", + " get_predictions(paragraph) for paragraph in document_data\n", + " ]\n", + " parsed_labels = parse_prediction_labels(raw_predictions)\n", + "\n", + " # 3. Filtering by dynamic label\n", + " filtered_entities = [\n", + " item\n", + " for item in parsed_labels\n", + " if item.get(\"aymurai_label\") == target_label\n", + " ]\n", + "\n", + " # 4. Saving individual file\n", + " clean_base_name = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", + " )\n", + " clean_base_name = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + " clean_name = re.sub(r\"-{2,}\", \"-\", clean_base_name)\n", + " file_name = f\"{clean_name}{file_ending}\"\n", + " save_path = output_path / file_name\n", + "\n", + " save_json(file_path=save_path, json_data=filtered_entities)\n", + " else:\n", + " for doc_path in tqdm(\n", + " document_paths, desc=f\"Extracting {target_label} entities\"\n", + " ):\n", + " doc_path = Path(doc_path)\n", + "\n", + " # 1. Load existing JSON data\n", + " document_data = load_json(doc_path)\n", + "\n", + " if not document_data:\n", + " print(f\"Warning: No document content found for {doc_path.name}\")\n", + " continue\n", + "\n", + " # 2. Filtering by dynamic label\n", + " filtered_entities = [\n", + " item\n", + " for item in document_data\n", + " if item.get(\"aymurai_label\") == target_label\n", + " ]\n", + "\n", + " # 3. Saving individual file\n", + " clean_base_name = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", + " )\n", + " clean_base_name = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + " clean_name = re.sub(r\"-{2,}\", \"-\", clean_base_name)\n", + " file_name = f\"{clean_name}{file_ending}\"\n", + " save_path = output_path / file_name\n", + "\n", + " save_json(file_path=save_path, json_data=filtered_entities)" + ] + }, + { + "cell_type": "markdown", + "id": "48e5096b", + "metadata": {}, + "source": [ + "## ***gold-jsons***" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1e21607", + "metadata": {}, + "outputs": [], + "source": [ + "# We save the gold_json in a directory in /canonical-entities/pre-clusterization\n", + "\n", + "if not (\n", + " DATA_ROOT.parent / \"canonical-entities\" / \"pre-clusterization\" / \"gold-jsons\"\n", + ").exists():\n", + " os.makedirs(\n", + " DATA_ROOT.parent / \"canonical-entities\" / \"pre-clusterization\" / \"gold-jsons\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "9fe4d751", + "metadata": {}, + "source": [ + "#### We identified that some .json files were incomplete due to missing entity_id fields. Consequently, we implemented a function to resolve this issue." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "466b7153", + "metadata": {}, + "outputs": [], + "source": [ + "def correct_entity_id(json_path: str) -> None:\n", + " \"\"\"Correct the entity_id field in the given JSON file to ensure it is a hex string.\"\"\"\n", + "\n", + " # Load existing canonical entities from JSON file\n", + " canonical_entities = load_json(json_path)\n", + "\n", + " # Convert reviewed entities back to CanonicalEntity objects\n", + " canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in canonical_entities\n", + " ]\n", + "\n", + " # Update entity_id to be a hex string\n", + " canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + " ]\n", + "\n", + " save_json(file_path=json_path, json_data=canonical_entities)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49e0a3c1", + "metadata": {}, + "outputs": [], + "source": [ + "data = load_json(gold_jsons[0])\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2896169", + "metadata": {}, + "outputs": [], + "source": [ + "correct_entity_id(gold_jsons[0])" + ] + }, + { + "cell_type": "markdown", + "id": "de9e145e", + "metadata": {}, + "source": [ + "#### Now we are ready to proceed with the ***gold_jsons***" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53242f47", + "metadata": {}, + "outputs": [], + "source": [ + "extract_and_save_entities(\n", + " document_paths=gold_jsons,\n", + " output_path=DATA_ROOT.parent\n", + " / \"canonical-entities\"\n", + " / \"pre-clusterization\"\n", + " / \"gold-jsons\",\n", + " file_ending=\"-gold.json\",\n", + " target_label=\"PER\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "93cf26e0", + "metadata": {}, + "source": [ + "------------------------ START OF DISCARD SECTION ------------------------" + ] + }, + { + "cell_type": "markdown", + "id": "dd7c2dcf", + "metadata": {}, + "source": [ + "### This was the old version to make the ***gold_json***, all of the entities in a single file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a02881f", + "metadata": {}, + "outputs": [], + "source": [ + "# DISCARD\n", + "def gold_json_labels(label: str, json_paths: list[Path]) -> list[dict[str, str]]:\n", + " g_json = [\n", + " item\n", + " for json_path in json_paths\n", + " for item in load_json(json_path)\n", + " if item.get(\"aymurai_label\") == label\n", + " ]\n", + "\n", + " return g_json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7ffdc3a", + "metadata": {}, + "outputs": [], + "source": [ + "# DISCARD\n", + "gold_json = gold_json_labels(label=\"PER\", json_paths=gold_jsons)\n", + "\n", + "save_json(\n", + " file_path=DATA_ROOT.parent\n", + " / \"canonical-entities\"\n", + " / \"pre-clusterization\"\n", + " / \"gold.json\",\n", + " json_data=gold_json,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "974128cb", + "metadata": {}, + "source": [ + "------------------------ END OF DISCARD SECTION ------------------------" + ] + }, + { + "cell_type": "markdown", + "id": "5e7933e4", + "metadata": {}, + "source": [ + "## ***ner-preds-jsons***" + ] + }, + { + "cell_type": "markdown", + "id": "58f10ce9", + "metadata": {}, + "source": [ + "#### Due to performance issues on macOS when processing all documents in a single loop, we processed them individually first to ensure they were correctly stored in the cache." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b986a569", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract document\n", + "session = requests.Session()\n", + "document = call_extraction_api(session, Path(documents[17]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21ab3461", + "metadata": {}, + "outputs": [], + "source": [ + "document = document.get(\"detail\", {}).get(\"document\")\n", + "\n", + "if not document:\n", + " raise ValueError(\"Document text is empty or not found.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4b2b2d5", + "metadata": {}, + "outputs": [], + "source": [ + "document" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef45ca9a", + "metadata": {}, + "outputs": [], + "source": [ + "# We save the gold_json in a directory in /canonical-entities/pre-clusterization\n", + "\n", + "if not (\n", + " DATA_ROOT.parent / \"canonical-entities\" / \"pre-clusterization\" / \"ner-preds-jsons\"\n", + ").exists():\n", + " os.makedirs(\n", + " DATA_ROOT.parent\n", + " / \"canonical-entities\"\n", + " / \"pre-clusterization\"\n", + " / \"ner-preds-jsons\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "f4c70898", + "metadata": {}, + "source": [ + "#### Now we are ready to proceed with the ***ner_preds_json***" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3f58d78", + "metadata": {}, + "outputs": [], + "source": [ + "extract_and_save_entities(\n", + " document_paths=documents,\n", + " output_path=DATA_ROOT.parent\n", + " / \"canonical-entities\"\n", + " / \"pre-clusterization\"\n", + " / \"ner-preds-jsons\",\n", + " file_ending=\"-ner-preds.json\",\n", + " target_label=\"PER\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e55481c4", + "metadata": {}, + "source": [ + "------------------------ START OF DISCARD SECTION ------------------------" + ] + }, + { + "cell_type": "markdown", + "id": "f0e34a8d", + "metadata": {}, + "source": [ + "#### The following cells belong to a previous version where all NER predictions were stored in a single input file. However, we discovered that certain entities appear in multiple files. Therefore, processing them in a single file would lead to incorrect comparisons against the ***gold_json***." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80045084", + "metadata": {}, + "outputs": [], + "source": [ + "# DISCARD\n", + "def get_entities_by_label(document_paths: list[Path], target_label: str) -> list[dict]:\n", + " \"\"\"\n", + " Iterates over a list of documents, extracts entities via API,\n", + " and returns a list of items matching the specified label.\n", + " \"\"\"\n", + " all_ner_preds = []\n", + "\n", + " # Using a session to reuse the connection for better performance\n", + " with requests.Session() as session:\n", + " for doc_path in tqdm(document_paths, desc=\"Processing documents\"):\n", + " # API Extraction\n", + " response = call_extraction_api(session, Path(doc_path))\n", + " document_data = response.get(\"detail\", {}).get(\"document\")\n", + "\n", + " if not document_data:\n", + " print(f\"Warning: No document content found for {doc_path}\")\n", + " continue\n", + "\n", + " # Get predictions for each paragraph\n", + " raw_predictions = [\n", + " get_predictions(paragraph) for paragraph in document_data\n", + " ]\n", + "\n", + " # Parse and flatten labels\n", + " parsed_labels = parse_prediction_labels(raw_predictions)\n", + "\n", + " # Filter by the selected label and extend the main list\n", + " filtered_entities = [\n", + " item\n", + " for item in parsed_labels\n", + " if item.get(\"aymurai_label\") == target_label\n", + " ]\n", + "\n", + " all_ner_preds.extend(filtered_entities)\n", + "\n", + " return all_ner_preds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7fb69feb", + "metadata": {}, + "outputs": [], + "source": [ + "# DISCARD\n", + "ner_preds_json = get_entities_by_label(document_paths=documents, target_label=\"PER\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8370add6", + "metadata": {}, + "outputs": [], + "source": [ + "# DISCARD\n", + "save_json(\n", + " file_path=DATA_ROOT.parent\n", + " / \"canonical-entities\"\n", + " / \"pre-clusterization\"\n", + " / \"ner_preds.json\",\n", + " json_data=ner_preds_json,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "05e4d66e", + "metadata": {}, + "source": [ + "------------------------ END OF DISCARD SECTION ------------------------" + ] + }, + { + "cell_type": "markdown", + "id": "d86a63e2", + "metadata": {}, + "source": [ + "## ***Evaluation Pipeline: Ground Truth and NER Prediction Pairing***\n", + "\n", + "We define the following function to ensure a correct implementation of the metrics evaluation for the performance of each combination of hyperparameters. The function returns each pair of .json paths for the clusterization and following evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02d574a8", + "metadata": {}, + "outputs": [], + "source": [ + "def pair_gold_and_preds(\n", + " gold_paths: list[Path], pred_paths: list[Path]\n", + ") -> list[Tuple[Path, Path]]:\n", + " \"\"\"\n", + " Pairs gold standard JSONs with their corresponding NER predictions\n", + " based on the common document prefix.\n", + " \"\"\"\n", + "\n", + " # 1. Helper to extract the core ID of the file. It takes everything before \"-ner-preds\" or \"-canonical-entities-gold\"\n", + " def get_document_id(path: Path) -> str:\n", + " name = path.stem\n", + " # Remove known suffixes to get the base ID\n", + " name = name.replace(\"-ner-preds\", \"\")\n", + " name = name.replace(\"-canonical-entities-gold\", \"\")\n", + " return name\n", + "\n", + " # 2. Create a mapping of {doc_id: pred_path}\n", + " preds_map = {get_document_id(p): p for p in pred_paths}\n", + "\n", + " paired_paths = []\n", + " missing_preds = []\n", + "\n", + " # 3. Iterate through gold files and find their match\n", + " for gold_path in gold_paths:\n", + " doc_id = get_document_id(gold_path)\n", + "\n", + " if doc_id in preds_map:\n", + " paired_paths.append((gold_path, preds_map[doc_id]))\n", + " else:\n", + " missing_preds.append(gold_path.name)\n", + "\n", + " # 4. Validation / Logging\n", + " if missing_preds:\n", + " print(f\"Warning: No predictions found for {len(missing_preds)} gold files.\")\n", + " for missing in missing_preds:\n", + " print(f\" - Missing match for: {missing}\")\n", + "\n", + " print(f\"Successfully paired {len(paired_paths)} documents.\")\n", + "\n", + " return paired_paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11af85fa", + "metadata": {}, + "outputs": [], + "source": [ + "PRE_CLUSTERIZATION_ROOT = GOLD_JSON_ROOT.parent / \"pre-clusterization\"\n", + "\n", + "gold_docs = discover_documents(\n", + " root=PRE_CLUSTERIZATION_ROOT / \"gold-jsons\", extensions=JSON_EXTENSION\n", + ")\n", + "pred_docs = discover_documents(\n", + " root=PRE_CLUSTERIZATION_ROOT / \"ner-preds-jsons\", extensions=JSON_EXTENSION\n", + ")\n", + "\n", + "document_pairs = pair_gold_and_preds(gold_docs, pred_docs)" + ] + }, + { + "cell_type": "markdown", + "id": "843b13b7", + "metadata": {}, + "source": [ + "### **Hyperparameter Grid Search & Evaluation Pipeline**\n", + "\n", + "We define a specialized function to orchestrate a **Grid Search** across multiple hyperparameters. The goal is to systematically evaluate the performance of our clustering model by testing every possible combination of **scorers**, **thresholds**, and **text processors**.\n", + "\n", + "#### **Pipeline Architecture**\n", + "\n", + "1. **Directory-Based Organization**:\n", + "For each unique combination of hyperparameters, the function creates a dedicated directory. The folder is named using the parameters (e.g., `scorer-cosine_threshold-0.8_proc-normalized`) to ensure clear traceability.\n", + "2. **Pre-Clustering Execution**:\n", + "Within each iteration, the pipeline processes the previously paired `gold-json` and `ner-preds-json` files. It generates a new `pre-cluster.json` file, preserving the original document's identity in the filename.\n", + "3. **Performance Evaluation**:\n", + "Once the cluster is generated, the function evaluates the results against the corresponding **Gold Standard**. This ensures that the metric reflects the actual accuracy of the current hyperparameter configuration.\n", + "4. **Iterative Metrics Logging**:\n", + "The evaluation results are stored in a centralized JSON file within the specific combination's folder.\n", + "* **Keys:** Represent the evaluated document name.\n", + "* **Values:** Represent the calculated performance metric.\n", + "This file is updated incrementally as the loop progresses through each document pair.\n", + "\n", + "\n", + "5. **Grid Progression**:\n", + "After all document pairs have been processed for a specific set of parameters, the function moves to the next grid cell, repeating the directory creation and evaluation steps until all combinations are exhausted." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f352cddd", + "metadata": {}, + "outputs": [], + "source": [ + "from rapidfuzz import fuzz, process, utils\n", + "from rapidfuzz.process import extractOne\n", + "from rapidfuzz.fuzz import (\n", + " ratio,\n", + " partial_ratio,\n", + " token_sort_ratio,\n", + " token_set_ratio,\n", + " partial_token_set_ratio,\n", + " partial_token_sort_ratio,\n", + " WRatio,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "559ffcac", + "metadata": {}, + "source": [ + "We take the functions defined by Juli in the `04-entity-disambiguation-from-pre-clustered-validations.ipynb` notebook and made one change:\n", + "- In `cluster_with_cdist` we added the text processor variable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b7b481e", + "metadata": {}, + "outputs": [], + "source": [ + "def cluster_with_cdist(\n", + " items: list[dict],\n", + " threshold: int = 90,\n", + " scorer: callable = token_set_ratio,\n", + " processor: callable = None,\n", + "):\n", + " \"\"\"\n", + " Cluster entities and prepare them for CanonicalEntity conversion.\n", + " \"\"\"\n", + " if not items:\n", + " return []\n", + "\n", + " # 1. Extract texts and apply normalization\n", + " # We keep track of the original text, the processed text, and the label\n", + " entities = [item.get(\"text\", \"\") for item in items]\n", + " labels = [item.get(\"aymurai_label\", \"UNKNOWN\") for item in items]\n", + "\n", + " if processor:\n", + " normed = [processor(e) for e in entities]\n", + " else:\n", + " normed = [str(e) for e in entities]\n", + "\n", + " # 2. Similarity Matrix\n", + " sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold)\n", + " sim = np.array(sim)\n", + "\n", + " # 3. Union-Find Logic\n", + " parent = list(range(len(normed)))\n", + "\n", + " def find(i):\n", + " if parent[i] == i:\n", + " return i\n", + " parent[i] = find(parent[i])\n", + " return parent[i]\n", + "\n", + " def union(i, j):\n", + " root_i, root_j = find(i), find(j)\n", + " if root_i != root_j:\n", + " parent[root_j] = root_i\n", + "\n", + " n = len(normed)\n", + " for i in range(n):\n", + " for j in range(i + 1, n):\n", + " if sim[i, j] >= threshold:\n", + " union(i, j)\n", + "\n", + " # 4. Group into the format parse_item expects: (orig, norm, label)\n", + " clusters_map = {}\n", + " for idx in range(n):\n", + " root = find(idx)\n", + " if root not in clusters_map:\n", + " clusters_map[root] = []\n", + " # This tuple matches your 'parse_item' len == 3 condition\n", + " clusters_map[root].append((entities[idx], normed[idx], labels[idx]))\n", + "\n", + " return list(clusters_map.values())\n", + "\n", + "\n", + "def parse_item(item: tuple[str, ...]) -> tuple[str, str, str]:\n", + " \"\"\"\n", + " Parse an item into (label, orig, norm).\n", + "\n", + " Accepts:\n", + " - (orig, norm, label)\n", + " - (labelled_orig, labelled_norm) with prefix 'LABEL:'\n", + " Args:\n", + " item (tuple[str, ...]): input item\n", + "\n", + " Returns:\n", + " tuple[str, str, str]: (label, orig, norm)\n", + " \"\"\"\n", + " if len(item) == 3:\n", + " orig, norm, label = item\n", + " return label, orig, norm\n", + "\n", + " # len == 2: assume \"LABEL:text\"\n", + " labelled_orig, labelled_norm = item\n", + " label, orig = labelled_orig.split(\":\", 1)\n", + " _, norm = labelled_norm.split(\":\", 1)\n", + " return label, orig, norm\n", + "\n", + "\n", + "def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Pick the most common label from parsed items.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen label\n", + " \"\"\"\n", + " labels = [lbl for lbl, _, _ in parsed_items]\n", + " # majority vote; fallback to first\n", + " return Counter(labels).most_common(1)[0][0]\n", + "\n", + "\n", + "def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Choose the longest original surface form; tweak as needed.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen canonical text\n", + " \"\"\"\n", + " return max(parsed_items, key=lambda x: len(x[1]))[1]\n", + "\n", + "\n", + "def clusters_to_canonical_entities(\n", + " clusters: list[list[tuple[str, str]]],\n", + ") -> list[CanonicalEntity]:\n", + " \"\"\"\n", + " Convert clusters to CanonicalEntity objects.\n", + "\n", + " Args:\n", + " clusters (list[list[tuple[str, str]]]): clusters of (original, normalized) entity tuples\n", + "\n", + " Returns:\n", + " list[CanonicalEntity]: list of CanonicalEntity objects\n", + " \"\"\"\n", + " canonical_entities = []\n", + "\n", + " for cluster in clusters:\n", + " parsed = [parse_item(item) for item in cluster] # [(label, orig, norm), ...]\n", + " label = pick_cluster_label(parsed)\n", + " canonical_text = pick_canonical_text(parsed)\n", + " aliases = sorted({orig for _, orig, _ in parsed})\n", + " ce = CanonicalEntity(\n", + " aymurai_label=label,\n", + " canonical_text=canonical_text,\n", + " aliases=aliases,\n", + " attributes={},\n", + " relations=[],\n", + " )\n", + " canonical_entities.append(ce)\n", + "\n", + " return canonical_entities" + ] + }, + { + "cell_type": "markdown", + "id": "afc1f54c", + "metadata": {}, + "source": [ + "We defined 2 new functions:\n", + "- `get_name` help us to get the cleaned name of the hyperparameters.\n", + "- `run_evaluation_grid_search` to run the grid search of the best hyperparameters combination.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3df974d", + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "\n", + "def get_name(obj):\n", + " \"\"\"Helper to extract a clean string name from a function or object.\"\"\"\n", + " if obj is None:\n", + " return \"None\"\n", + " if hasattr(obj, \"__name__\"):\n", + " return obj.__name__\n", + " # Fallback for complex objects or partials: remove memory addresses\n", + " clean_name = re.sub(r\" at 0x[0-9a-fA-F]+\", \"\", str(obj))\n", + " return clean_name.strip(\"<>\").replace(\"cyfunction \", \"\").replace(\"function \", \"\")\n", + "\n", + "\n", + "def run_evaluation_grid_search(\n", + " scorers: list,\n", + " thresholds: list,\n", + " processors: list,\n", + " document_pairs: list,\n", + " target_label: str,\n", + " base_output_path: Path,\n", + "):\n", + " \"\"\"\n", + " Runs a grid search over hyperparameters using nested loops.\n", + " \"\"\"\n", + "\n", + " # To keep track of all results and find the best\n", + " all_combinations_results = []\n", + "\n", + " for scorer in scorers:\n", + " for threshold in thresholds:\n", + " for processor in processors:\n", + " # 1. Create a descriptive folder name with the cleaned names\n", + " scorer_name = get_name(scorer)\n", + " processor_name = get_name(processor)\n", + " combo_name = (\n", + " f\"scorer-{scorer_name}-threshold-{threshold}-proc-{processor_name}\"\n", + " )\n", + "\n", + " combo_dir = base_output_path / combo_name\n", + " combo_dir.mkdir(parents=True, exist_ok=True)\n", + "\n", + " metrics_results = {}\n", + " metrics_file_path = (\n", + " combo_dir / f\"evaluation_metrics_{target_label}.json\"\n", + " )\n", + "\n", + " # List to collect scores for this specific combination\n", + " current_combo_scores = []\n", + "\n", + " # 2. Iterate over document pairs (Gold vs Prediction)\n", + " for gold_path, pred_path in tqdm(\n", + " document_pairs, desc=f\"Testing {combo_name}\"\n", + " ):\n", + " # Extract entities from the JSON, we filter by target label to ensure consistency if the file has multiple labels\n", + " entities = load_json(pred_path)\n", + " filtered_preds = [\n", + " p for p in entities if p[\"aymurai_label\"] == target_label\n", + " ]\n", + "\n", + " # --- Pre-clustering Logic ---\n", + " # Run the clustering using your hyperparameters\n", + " cluster_data = cluster_with_cdist(\n", + " items=filtered_preds,\n", + " threshold=threshold,\n", + " scorer=scorer,\n", + " processor=processor,\n", + " )\n", + "\n", + " # Convert to Canonical Entities\n", + " canonical_entities = clusters_to_canonical_entities(cluster_data)\n", + "\n", + " canonical_entities = [\n", + " CanonicalEntity.model_validate(entity)\n", + " for entity in canonical_entities\n", + " ]\n", + "\n", + " canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + " ]\n", + "\n", + " # Save the result as the pre-cluster.json\n", + " cluster_filename = f\"{pred_path.stem}-pre-cluster.json\"\n", + " cluster_output_path = combo_dir / cluster_filename\n", + "\n", + " save_json(\n", + " file_path=cluster_output_path, json_data=canonical_entities\n", + " )\n", + "\n", + " # --- Metric Evaluation ---\n", + " score, metrics = aymurai_metrics.evaluate_disambiguation(\n", + " gold_json=load_json(gold_path),\n", + " pred_json=load_json(cluster_output_path),\n", + " target_label=target_label,\n", + " )\n", + "\n", + " current_combo_scores.append(score)\n", + " doc_id = pred_path.stem\n", + " metrics_results[doc_id] = {\n", + " \"label\": target_label,\n", + " \"metric_value\": score,\n", + " \"detailed_metrics\": metrics,\n", + " }\n", + "\n", + " # --- Calculate Average for this combination ---\n", + " avg_score = (\n", + " np.mean(current_combo_scores) if current_combo_scores else 0.0\n", + " )\n", + "\n", + " # Add average to the results file for this folder\n", + " final_output = {\n", + " \"average_combination_score\": avg_score,\n", + " \"document_details\": metrics_results,\n", + " }\n", + "\n", + " with open(metrics_file_path, \"w\") as f:\n", + " json.dump(final_output, f, indent=4)\n", + "\n", + " # Store combination info for final ranking\n", + " all_combinations_results.append(\n", + " {\n", + " \"name\": combo_name,\n", + " \"score\": avg_score,\n", + " \"params\": {\n", + " \"scorer\": scorer_name,\n", + " \"threshold\": threshold,\n", + " \"processor\": processor_name,\n", + " },\n", + " }\n", + " )\n", + "\n", + " # Save all combinations results in a summary file\n", + " summary_file_path = (\n", + " base_output_path / f\"results_summary_{target_label}.json\"\n", + " )\n", + " with open(summary_file_path, \"w\") as f:\n", + " json.dump(all_combinations_results, f, indent=4)\n", + "\n", + " # --- Find the best combination ---\n", + " best_combo = max(all_combinations_results, key=lambda x: x[\"score\"])\n", + "\n", + " print(f\"\\nGrid Search for {target_label} completed.\")\n", + "\n", + " # We now copy the best combination folder to a new location with a standardized name\n", + " src = Path(base_output_path) / best_combo[\"name\"]\n", + " # Combine the parent destination path with the new folder name\n", + " new_name = f\"best-pre-clusterization-{target_label.lower()}\"\n", + " dest = Path(base_output_path.parent) / new_name\n", + " \n", + " try:\n", + " # copytree creates the destination directory with the 'new_name'\n", + " shutil.copytree(src, dest)\n", + " print(f\"Successfully copied '{src.name}' to '{dest}'\")\n", + " except FileExistsError:\n", + " print(f\"Error: A folder named '{new_name}' already exists in '{base_output_path.parent}'\")\n", + " except Exception as e:\n", + " print(f\"An error occurred: {e}\") \n", + "\n", + " return best_combo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6369c582", + "metadata": {}, + "outputs": [], + "source": [ + "# We have to install jellyfish for the phonetic processor\n", + "!pip install jellyfish" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f48e9d4c", + "metadata": {}, + "outputs": [], + "source": [ + "def hard_normalizer(s: str) -> str:\n", + " \"\"\"\n", + " Normalize string for clustering: strips accents, removes punctuation (.,-),\n", + " lowercases, and collapses spaces.\n", + "\n", + " Args:\n", + " s (str): input string\n", + " Returns:\n", + " str: normalized string\n", + " \"\"\"\n", + " if not s:\n", + " return \"\"\n", + "\n", + " # Strip accents\n", + " s = \"\".join(\n", + " c for c in unicodedata.normalize(\"NFD\", s) if unicodedata.category(c) != \"Mn\"\n", + " )\n", + "\n", + " # Remove commas, periods, and hyphens\n", + " s = re.sub(r\"[.,\\-]\", \" \", s)\n", + "\n", + " # Lowercase and collapse whitespace\n", + " s = \" \".join(s.lower().split())\n", + "\n", + " return s\n", + "\n", + "def light_normalizer(s: str) -> str:\n", + " return s.lower().strip() if s else \"\"\n", + "\n", + "\n", + "def legal_text_normalizer(s: str) -> str:\n", + " if not s:\n", + " return \"\"\n", + "\n", + " # Standard cleaning (accents/lowercase)\n", + " s = \"\".join(\n", + " c for c in unicodedata.normalize(\"NFD\", s) if unicodedata.category(c) != \"Mn\"\n", + " )\n", + " s = s.lower()\n", + "\n", + " # Remove Legal Titles & Prefixes\n", + " legal_titles = r\"\\b(dr|dra|sr|sra|expte|nro|no|pcia)\\b\\.?\"\n", + " s = re.sub(legal_titles, \"\", s)\n", + "\n", + " # Remove common Spanish stopwords\n", + " stopwords = r\"\\b(de|del|la|las|el|los|y|en)\\b\"\n", + " s = re.sub(stopwords, \"\", s)\n", + "\n", + " # Remove all non-alphanumeric except spaces\n", + " s = re.sub(r\"[^\\w\\s]\", \"\", s)\n", + "\n", + " return \" \".join(s.split())\n", + "\n", + "\n", + "import jellyfish\n", + "\n", + "\n", + "def phonetic_normalizer(s: str) -> str:\n", + " if not s:\n", + " return \"\"\n", + " # Standardize first\n", + " clean = \"\".join(\n", + " c for c in unicodedata.normalize(\"NFD\", s) if unicodedata.category(c) != \"Mn\"\n", + " ).lower()\n", + "\n", + " return jellyfish.nysiis(clean)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f641f4a", + "metadata": {}, + "outputs": [], + "source": [ + "scorers = [\n", + " ratio,\n", + " partial_ratio,\n", + " token_sort_ratio,\n", + " token_set_ratio,\n", + " partial_token_set_ratio,\n", + " partial_token_sort_ratio,\n", + " WRatio,\n", + "]\n", + "\n", + "thresholds = list(range(50, 100, 5))\n", + "\n", + "processors = [\n", + " None,\n", + " hard_normalizer,\n", + " light_normalizer,\n", + " legal_text_normalizer,\n", + " phonetic_normalizer,\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6562159d", + "metadata": {}, + "outputs": [], + "source": [ + "top_result = run_evaluation_grid_search(\n", + " scorers=scorers,\n", + " thresholds=thresholds,\n", + " processors=processors,\n", + " document_pairs=document_pairs,\n", + " target_label=\"PER\",\n", + " base_output_path=PRE_CLUSTERIZATION_ROOT / \"grid-search-results\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a8eecab", + "metadata": {}, + "outputs": [], + "source": [ + "print(\n", + " f\"The best hyperparameter combination is '{top_result['name']}' \"\n", + " f\"with an average score of {top_result['score']:.4f}.\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6aae6a7b", + "metadata": {}, + "outputs": [], + "source": [ + "gold_path = PRE_CLUSTERIZATION_ROOT / \"gold-jsons\"\n", + "\n", + "pred_path = PRE_CLUSTERIZATION_ROOT / \"grid-search-results\" / top_result[\"name\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cfbd11f", + "metadata": {}, + "outputs": [], + "source": [ + "result_validation_results, result_validation_avg_score = (\n", + " aymurai_metrics.evaluate_prediction_directories(\n", + " gold_dir=gold_path,\n", + " preds_dir=pred_path,\n", + " target_label=\"PER\",\n", + " pred_json_suffix=\"-ner-preds-pre-cluster\",\n", + " gold_json_suffix=\"-canonical-entities-gold\",\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0bc466c9", + "metadata": {}, + "outputs": [], + "source": [ + "result_validation_avg_score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f484a022", + "metadata": {}, + "outputs": [], + "source": [ + "result_validation_results" + ] + }, + { + "cell_type": "markdown", + "id": "86a21ede", + "metadata": {}, + "source": [ + "Check one of the pre-clusterization in one of the documents because there is only one cluster" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "163ecabc", + "metadata": {}, + "outputs": [], + "source": [ + "json_to_check = load_json(json_file_path=document_pairs[1][1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a00fc29", + "metadata": {}, + "outputs": [], + "source": [ + "pre_cluster_json = cluster_with_cdist(\n", + " items=json_to_check,\n", + " threshold=50,\n", + " scorer=partial_token_set_ratio,\n", + " processor=hard_normalizer,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "670abaf8", + "metadata": {}, + "outputs": [], + "source": [ + "# Convert to Canonical Entities\n", + "canonical_entities = clusters_to_canonical_entities(pre_cluster_json)\n", + "\n", + "canonical_entities = [\n", + " CanonicalEntity.model_validate(entity) for entity in canonical_entities\n", + "]\n", + "\n", + "canonical_entities = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in canonical_entities\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c15e0a2", + "metadata": {}, + "outputs": [], + "source": [ + "canonical_entities" + ] + }, + { + "cell_type": "markdown", + "id": "844f939a", + "metadata": {}, + "source": [ + "We are now analyzing the similarity matrix to identify items connected to multiple neighbors. By leveraging the Union-Find algorithm, we can determine if these local connections form a chain or tree structure that merges all entities into a single global cluster." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e9a0b46", + "metadata": {}, + "outputs": [], + "source": [ + "processor = light_normalizer\n", + "\n", + "item = load_json(document_pairs[1][1])\n", + "\n", + "entities = [item.get(\"text\", \"\") for item in item]\n", + "labels = [item.get(\"aymurai_label\", \"UNKNOWN\") for item in item]\n", + "\n", + "if processor:\n", + " normed = [processor(e) for e in entities]\n", + "else:\n", + " normed = [str(e) for e in entities]\n", + "\n", + "sim = process.cdist(normed, normed, scorer=partial_token_set_ratio, score_cutoff=50)\n", + "sim = np.array(sim)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95270c37", + "metadata": {}, + "outputs": [], + "source": [ + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.colors import ListedColormap\n", + "import numpy as np\n", + "\n", + "# Create a binary version for visualization: 0 if sim <= 0 else 1\n", + "viz_matrix = (sim > 0).astype(int)\n", + "\n", + "# Define the colormap: 0 -> white, 1 -> red\n", + "my_cmap = ListedColormap([\"white\", \"red\"])\n", + "\n", + "mask = np.triu(np.ones_like(sim, dtype=bool))\n", + "\n", + "# Plot the matrix using the binary color logic\n", + "plt.figure(figsize=(6, 6))\n", + "sns.heatmap(\n", + " viz_matrix,\n", + " cmap=my_cmap,\n", + " mask=mask,\n", + " cbar=False,\n", + " linewidths=0.5,\n", + " linecolor=\"lightgray\",\n", + " square=True, # Ensures cells are perfectly square\n", + ")\n", + "\n", + "plt.title(\"Connection Visualization (Red if > 0)\", fontsize=15)\n", + "plt.xlabel(\"Entity Index\")\n", + "plt.ylabel(\"Entity Index\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb8d30cc", + "metadata": {}, + "outputs": [], + "source": [ + "item[24]" + ] + }, + { + "cell_type": "markdown", + "id": "947379d0", + "metadata": {}, + "source": [ + "Based on the similarity matrix, there is a high probability that the algorithm will generate a single \"giant cluster\" due to a phenomenon known as **chaining**.\n", + "\n", + "Observation of the similarity matrix shows that **item [24]** (identified as an **\"L\"**) and **item [0]** are almost entirely highlighted in red. This indicates that these entities maintain a similarity score above the threshold with nearly every other entity in the set.\n", + "* **Union-Find Logic:** Because the pipeline utilizes a **Union-Find (Disjoint Set Union)** algorithm, a single \"bridge\" is sufficient to merge two distinct groups. In this case, item [24] acts as a universal bridge.\n", + "* **Cluster Collapse:** Since item [24] is connected to almost all other nodes, the algorithm will transitively link every entity into a single tree structure, eventually collapsing the entire dataset into one oversized cluster." + ] + }, + { + "cell_type": "markdown", + "id": "d3b5b315", + "metadata": {}, + "source": [ + "## ***CONCLUSION***: Hyperparameter Optimization Results\n", + "\n", + "After a systematic grid search across various scorers, **`token_set_ratio`** was identified as the top-performing configuration. The following analysis breaks down why this method provided the best balance for our entity disambiguation task:\n", + "\n", + "#### 1. Robustness to Context and Alias Matching (The \"Set\" Advantage)\n", + "\n", + "Unlike character-level scorers (like `ratio` or `partial_ratio`), `token_set_ratio` treats strings as unordered collections of words (tokens). \n", + "\n", + "* **Handling Name Aliases:** It is particularly effective at linking different versions of a person's name. For example, it recognizes that **\"Juan Pérez\"** and **\"Juan Alberto Pérez\"** refer to the same entity by identifying the shared tokens (\"Juan\", \"Pérez\") as a subset, yielding a high similarity score despite the additional middle name.\n", + "* **The \"Intersection\" Logic:** This is crucial for our dataset when a name appears as \"Juan Pérez\" in one document and \"Juan Pérez Asesor/a\" in another (where the NER might have accidentally included the role).\n", + "* **Subset Resilience:** It yields a score of 100 if one name is a perfect subset of the other, effectively ignoring \"noise\" words or titles that often appear in raw NER extractions.\n", + "\n", + "#### 2. Superior Performance over `token_sort_ratio`\n", + "\n", + "While `token_sort_ratio` is excellent at handling word reordering (e.g., \"Juan Pérez\" vs. \"Pérez, Juan\"), it is highly sensitive to the length of the string. \n", + "* In scenarios involving **aliases with extra names** (e.g., \"Juan Pérez\" vs. \"Juan Alberto Pérez\") or **titles** (e.g., \"Doctor Juan Pérez\"), `token_sort_ratio` would penalize the score significantly because the total word count differs. \n", + "* **`token_set_ratio`** maintains a high similarity score in these cases by prioritizing the common intersection of words over the total string length.\n", + "\n", + "#### 3. Preventing Over-Clustering (Avoidance of \"Partial\" Aggression)\n", + "\n", + "The grid search revealed that more aggressive scorers, such as `partial_token_set_ratio`, often led to **over-clustering** and data pollution.\n", + "\n", + "* **The Risk of \"Partial\" Logic:** These scorers are too \"patient\" with differences. If they find a small, similar fragment within a much longer, unrelated string, they force a high score. \n", + "* **Chaining Effect:** This was the primary cause of the \"Chaining Effect\" (as seen with **Item [24]**), where a single-letter entity or a common word acts as a universal bridge, transitively merging hundreds of unrelated individuals into a single \"giant cluster.\"\n", + "* **The `token_set_ratio` Balance:** It provides the necessary flexibility to catch legitimate aliases without being so aggressive that it collapses the distinct identities of the entire corpus.\n", + "\n", + "### Final Recommendation\n", + "\n", + "The **`token_set_ratio`** achieved the highest **Weighted Disambiguation Score** because it effectively handles variable-length mentions, middle names, and word-order changes while maintaining a strict enough threshold to avoid the transitive merging of unrelated person entities." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb new file mode 100644 index 00000000..6af18cea --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb @@ -0,0 +1,1487 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "64957296", + "metadata": {}, + "source": [ + "from previous notebook `06-pre-disambiguation-optimization` we found that the best hyperparameters combinations are:\n", + "- scorer: `token_set_ratio`\n", + "- score_cutoff: `70`\n", + "- processor: `light_normalizer`\n", + "\n", + "we now proceed to make the canonical entities disambiguation checked by an LLM and we give the task to the LLM to give a role to each canonical entity when it is related to a person." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb2b1f59", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "607ea227", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import mimetypes\n", + "import os\n", + "import re\n", + "import time\n", + "import unicodedata\n", + "from collections import Counter\n", + "from operator import itemgetter\n", + "from pathlib import Path\n", + "from typing import Iterable, Tuple\n", + "\n", + "import requests\n", + "from tqdm import tqdm\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import requests\n", + "from more_itertools import flatten, unique_everseen\n", + "\n", + "\n", + "from aymurai.llm_providers import OllamaLLMProvider\n", + "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", + "from aymurai.utils.json_data import get_pretty, save_json, load_json\n", + "import aymurai.evaluation.metrics as aymurai_metrics\n", + "\n", + "from rapidfuzz.fuzz import token_set_ratio\n", + "from rapidfuzz import process" + ] + }, + { + "cell_type": "markdown", + "id": "1003fbb5", + "metadata": {}, + "source": [ + "# #**1** First Step: Prepare the ***gold_json*** and ***ner_preds_json***" + ] + }, + { + "cell_type": "markdown", + "id": "7ec52f24", + "metadata": {}, + "source": [ + "## /document-extract endpoint output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7c3b672", + "metadata": {}, + "outputs": [], + "source": [ + "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://127.0.0.1:8000\")\n", + "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", + "DATA_ROOT = Path(\n", + " os.getenv(\n", + " \"DOCUMENT_DATA_ROOT\", \"../../../resources/data/restricted/disambiguation-eval/files\"\n", + " )\n", + ")\n", + "GOLD_JSON_ROOT = Path(\n", + " os.getenv(\n", + " \"GOLD_JSON_ROOT\",\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted\",\n", + " )\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "JSON_EXTENSION = {\".json\"}\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18a31502", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "print(f\"Discovered {len(documents)} documents.\")\n", + "\n", + "gold_jsons = discover_documents(GOLD_JSON_ROOT, JSON_EXTENSION)\n", + "print(f\"Discovered {len(gold_jsons)} gold JSON files.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65a2d588", + "metadata": {}, + "outputs": [], + "source": [ + "def call_extraction_api(\n", + " session: requests.Session, file_path: Path\n", + ") -> dict[str, object]:\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " ENDPOINT,\n", + " files=files,\n", + " timeout=REQUEST_TIMEOUT_S,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload" + ] + }, + { + "cell_type": "markdown", + "id": "dcaed02f", + "metadata": {}, + "source": [ + "## Inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "464e9132", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import chain\n", + "from operator import itemgetter\n", + "from typing import Any\n", + "\n", + "from more_itertools import unique_everseen\n", + "\n", + "\n", + "# Function to make inference using the API\n", + "def get_predictions(sample: str) -> dict:\n", + " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample})\n", + " response.raise_for_status()\n", + " return response.json()\n", + "\n", + "\n", + "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", + " \"\"\"\n", + " Parse prediction labels to extract unique aymurai_label and aymurai_alt_text pairs.\n", + "\n", + " Args:\n", + " predictions (list[dict[str, Any]]): A list of prediction dictionaries.\n", + "\n", + " Returns:\n", + " list[dict[str, str]]: A list of dictionaries containing unique aymurai_label and aymurai_alt_text pairs.\n", + " \"\"\"\n", + " attrs_stream = (\n", + " label.get(\"attrs\") or {}\n", + " for label in chain.from_iterable(pred.get(\"labels\", ()) for pred in predictions)\n", + " )\n", + "\n", + " unique_pairs = unique_everseen(\n", + " (\n", + " attrs.get(\"aymurai_label\"),\n", + " attrs.get(\"aymurai_alt_text\"),\n", + " )\n", + " for attrs in attrs_stream\n", + " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", + " )\n", + "\n", + " return sorted(\n", + " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", + " key=itemgetter(\"aymurai_label\", \"text\"),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "b690bfc0", + "metadata": {}, + "source": [ + "## ***gold_json*** and ***ner_preds_json*** for input to clusterization developement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b37a8ac", + "metadata": {}, + "outputs": [], + "source": [ + "def extract_and_save_entities(\n", + " document_paths: list[Path], output_path: Path, file_ending: str, target_label: str\n", + ") -> None:\n", + " \"\"\"\n", + " Processes a list of documents to extract specific entities and saves\n", + " each result as an individual JSON file.\n", + " It has been optimized to handle both document files and pre-existing JSON files.\n", + " \"\"\"\n", + "\n", + " with requests.Session() as session:\n", + " if \".json\" not in document_paths[0].suffix:\n", + " for doc_path in tqdm(\n", + " document_paths, desc=f\"Extracting {target_label} entities\"\n", + " ):\n", + " doc_path = Path(doc_path)\n", + "\n", + " # 1. API Extraction\n", + " response = call_extraction_api(session, doc_path)\n", + " document_data = response.get(\"detail\", {}).get(\"document\")\n", + "\n", + " if not document_data:\n", + " print(f\"Warning: No document content found for {doc_path.name}\")\n", + " continue\n", + "\n", + " # 2. Processing\n", + " raw_predictions = [\n", + " get_predictions(paragraph) for paragraph in document_data\n", + " ]\n", + " parsed_labels = parse_prediction_labels(raw_predictions)\n", + "\n", + " # 3. Filtering by dynamic label\n", + " filtered_entities = [\n", + " item\n", + " for item in parsed_labels\n", + " if item.get(\"aymurai_label\") == target_label\n", + " ]\n", + "\n", + " # 4. Saving individual file\n", + " clean_base_name = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", + " )\n", + " clean_base_name = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + " clean_name = re.sub(r\"-{2,}\", \"-\", clean_base_name)\n", + " file_name = f\"{clean_name}{file_ending}\"\n", + " save_path = output_path / file_name\n", + "\n", + " save_json(file_path=save_path, json_data=filtered_entities)\n", + " else:\n", + " for doc_path in tqdm(\n", + " document_paths, desc=f\"Extracting {target_label} entities\"\n", + " ):\n", + " doc_path = Path(doc_path)\n", + "\n", + " # 1. Load existing JSON data\n", + " document_data = load_json(doc_path)\n", + "\n", + " if not document_data:\n", + " print(f\"Warning: No document content found for {doc_path.name}\")\n", + " continue\n", + "\n", + " # 2. Filtering by dynamic label\n", + " filtered_entities = [\n", + " item\n", + " for item in document_data\n", + " if item.get(\"aymurai_label\") == target_label\n", + " ]\n", + "\n", + " # 3. Saving individual file\n", + " clean_base_name = re.sub(\n", + " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", + " )\n", + " clean_base_name = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + " clean_name = re.sub(r\"-{2,}\", \"-\", clean_base_name)\n", + " file_name = f\"{clean_name}{file_ending}\"\n", + " save_path = output_path / file_name\n", + "\n", + " save_json(file_path=save_path, json_data=filtered_entities)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4824a9c3", + "metadata": {}, + "outputs": [], + "source": [ + "def cluster_with_cdist(\n", + " items: list[dict],\n", + " threshold: int = 90,\n", + " scorer: callable = token_set_ratio,\n", + " processor: callable = None,\n", + "):\n", + " \"\"\"\n", + " Cluster entities and prepare them for CanonicalEntity conversion.\n", + " \"\"\"\n", + " if not items:\n", + " return []\n", + "\n", + " # 1. Extract texts and apply normalization\n", + " # We keep track of the original text, the processed text, and the label\n", + " entities = [item.get(\"text\", \"\") for item in items]\n", + " labels = [item.get(\"aymurai_label\", \"UNKNOWN\") for item in items]\n", + "\n", + " if processor:\n", + " normed = [processor(e) for e in entities]\n", + " else:\n", + " normed = [str(e) for e in entities]\n", + "\n", + " # 2. Similarity Matrix\n", + " sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold)\n", + " sim = np.array(sim)\n", + "\n", + " # 3. Union-Find Logic\n", + " parent = list(range(len(normed)))\n", + "\n", + " def find(i):\n", + " if parent[i] == i:\n", + " return i\n", + " parent[i] = find(parent[i])\n", + " return parent[i]\n", + "\n", + " def union(i, j):\n", + " root_i, root_j = find(i), find(j)\n", + " if root_i != root_j:\n", + " parent[root_j] = root_i\n", + "\n", + " n = len(normed)\n", + " for i in range(n):\n", + " for j in range(i + 1, n):\n", + " if sim[i, j] >= threshold:\n", + " union(i, j)\n", + "\n", + " # 4. Group into the format parse_item expects: (orig, norm, label)\n", + " clusters_map = {}\n", + " for idx in range(n):\n", + " root = find(idx)\n", + " if root not in clusters_map:\n", + " clusters_map[root] = []\n", + " # This tuple matches your 'parse_item' len == 3 condition\n", + " clusters_map[root].append((entities[idx], normed[idx], labels[idx]))\n", + "\n", + " return list(clusters_map.values())\n", + "\n", + "\n", + "def parse_item(item: tuple[str, ...]) -> tuple[str, str, str]:\n", + " \"\"\"\n", + " Parse an item into (label, orig, norm).\n", + "\n", + " Accepts:\n", + " - (orig, norm, label)\n", + " - (labelled_orig, labelled_norm) with prefix 'LABEL:'\n", + " Args:\n", + " item (tuple[str, ...]): input item\n", + "\n", + " Returns:\n", + " tuple[str, str, str]: (label, orig, norm)\n", + " \"\"\"\n", + " if len(item) == 3:\n", + " orig, norm, label = item\n", + " return label, orig, norm\n", + "\n", + " # len == 2: assume \"LABEL:text\"\n", + " labelled_orig, labelled_norm = item\n", + " label, orig = labelled_orig.split(\":\", 1)\n", + " _, norm = labelled_norm.split(\":\", 1)\n", + " return label, orig, norm\n", + "\n", + "\n", + "def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Pick the most common label from parsed items.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen label\n", + " \"\"\"\n", + " labels = [lbl for lbl, _, _ in parsed_items]\n", + " # majority vote; fallback to first\n", + " return Counter(labels).most_common(1)[0][0]\n", + "\n", + "\n", + "def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str:\n", + " \"\"\"\n", + " Choose the longest original surface form; tweak as needed.\n", + "\n", + " Args:\n", + " parsed_items (list[tuple[str, str, str]]): parsed items\n", + "\n", + " Returns:\n", + " str: chosen canonical text\n", + " \"\"\"\n", + " return max(parsed_items, key=lambda x: len(x[1]))[1]\n", + "\n", + "\n", + "def clusters_to_canonical_entities(\n", + " clusters: list[list[tuple[str, str]]],\n", + ") -> list[CanonicalEntity]:\n", + " \"\"\"\n", + " Convert clusters to CanonicalEntity objects.\n", + "\n", + " Args:\n", + " clusters (list[list[tuple[str, str]]]): clusters of (original, normalized) entity tuples\n", + "\n", + " Returns:\n", + " list[CanonicalEntity]: list of CanonicalEntity objects\n", + " \"\"\"\n", + " canonical_entities = []\n", + "\n", + " for cluster in clusters:\n", + " parsed = [parse_item(item) for item in cluster] # [(label, orig, norm), ...]\n", + " label = pick_cluster_label(parsed)\n", + " canonical_text = pick_canonical_text(parsed)\n", + " aliases = sorted({orig for _, orig, _ in parsed})\n", + " ce = CanonicalEntity(\n", + " aymurai_label=label,\n", + " canonical_text=canonical_text,\n", + " aliases=aliases,\n", + " attributes={},\n", + " relations=[],\n", + " )\n", + " canonical_entities.append(ce)\n", + "\n", + " return canonical_entities\n", + "\n", + "\n", + "def light_normalizer(s: str) -> str:\n", + " return s.lower().strip() if s else \"\"" + ] + }, + { + "cell_type": "markdown", + "id": "f2aa5d15", + "metadata": {}, + "source": [ + "## Canonical Entity extraction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8220314e", + "metadata": {}, + "outputs": [], + "source": [ + "# Available models\n", + "\n", + "models = [\n", + " \"phi4:14b\",\n", + " \"gpt-oss:20b\",\n", + " \"llama3\",\n", + " \"llama3.1:8b\",\n", + " \"gemma3:270m\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82f8d1a2", + "metadata": {}, + "outputs": [], + "source": [ + "# Sanity check\n", + "provider = OllamaLLMProvider(model=models[0])\n", + "provider.generate(\"hola, ¿cómo estás?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f40ef8ed", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_1 = \"\"\"\n", + "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", + "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", + "\n", + "# Material de Trabajo\n", + "Para tu análisis, recibirás:\n", + "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", + "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", + "\n", + "# Tus Tareas\n", + "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", + "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", + "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", + "\n", + "# Reglas de Salida\n", + "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", + "- `canonical_text`: El nombre más completo y formal encontrado.\n", + "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Defensor/a de Cámara\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", + "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", + "\n", + "# Estructura de Respuesta (JSON)\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Alias Principal\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\n", + "# Ejemplo de Input\n", + "Contexto: \"...la declaración del Sr. Juan Carlos Ruiz ante este Juzgado. El imputado Ruiz negó los cargos. Por su parte, la Dra. Elena Sosa, fiscal de la causa, solicitó...\"\n", + "Entidades Candidatas:\n", + "[{\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Juan Carlos Ruiz\",\n", + " \"aliases\": [\n", + " \"Juan Carlos Ruiz\",\n", + " \"Ruiz\"\n", + " ]\n", + "},\n", + "{\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Elena Sosa\",\n", + " \"aliases\": [\n", + " \"Elena Sosa\"\n", + " ]\n", + "}]\n", + "\n", + "# Ejemplo de Output\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Juan Carlos Ruiz\",\n", + " \"aliases\": [\n", + " \"Juan Carlos Ruiz\",\n", + " \"Ruiz\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Imputado\"\n", + " }\n", + " },\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Elena Sosa\",\n", + " \"aliases\": [\n", + " \"Elena Sosa\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Fiscal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40c5f5ae", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_2 = \"\"\"\n", + "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", + "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", + "\n", + "# Material de Trabajo\n", + "Para tu análisis, recibirás:\n", + "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", + "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", + "\n", + "# Tus Tareas\n", + "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", + "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", + "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", + "\n", + "# Reglas de Salida\n", + "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", + "- `canonical_text`: El nombre más completo y formal encontrado.\n", + "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Defensor/a de Cámara\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", + "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", + "\n", + "# Estructura de Respuesta (JSON)\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Alias Principal\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3f2848b", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template_1 = \"\"\"\n", + "A continuación se proporcionan fragmentos relevantes de un documento judicial y una lista de entidades (personas) pre-clusterizadas por similitud de texto.\n", + "\n", + "# Contexto de la causa, párrafos con alguna mención a las personas:\n", + "{document_text}\n", + "\n", + "# Entidades Pre-clusterizadas a validar:\n", + "{canonical_entities}\n", + "\n", + "# Instrucciones:\n", + "1. **Validación de Aliases:** Analiza si los aliases dentro de cada entidad canónica pertenecen realmente a la misma persona según los párrafos de contexto. \n", + " - Si un alias pertenece a una persona distinta (ej. un familiar con nombre similar), sepáralo en una nueva entidad.\n", + " - Si dos grupos de entidades canónicas refieren a la misma persona, fusiónalos.\n", + "2. **Identificación de Roles:** Extrae el rol procesal de cada persona basándote en el contexto (ej: Juez/a, Fiscal, Denunciante, Imputado/a, Víctima, Abogado/a, Perito/a, Testigo).\n", + "3. **Normalización:** Define el `canonical_text` como el nombre más completo y formal que aparezca en los aliases, si este trabajo ya está bien hecho no lo modifiques.\n", + "4. **Formato:** Devuelve la lista final de entidades en el formato JSON solicitado en el system prompt, asegurándote de no omitir a nadie que sea relevante.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d9365ca", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_3 = \"\"\"\n", + "Eres un asistente experto en desambiguación de entidades judiciales.\n", + "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", + "\n", + "### Tu Tarea:\n", + "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", + "2. **Limpiar Aliases:** Elimina prefijos (Dr., Sra., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", + "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", + "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + " - **Nota:** No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica rol, usa `null`.\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve exclusivamente un array de objetos:\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Alias Principal\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f7bf0f7", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_4 = \"\"\"\n", + "Eres un asistente experto en desambiguación de entidades judiciales.\n", + "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", + "\n", + "### Tu Tarea:\n", + "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", + "2. **Limpiar Aliases:** Elimina prefijos (Dr., Dra., Dres., Sra., Sr., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", + "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", + "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + " \n", + " **Instrucciones críticas de rol:**\n", + " - Si una persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y el contexto no indica que es Juez, Fiscal o Defensor oficial, asígnale el rol de **\"Abogado/a\"**.\n", + " - No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica un rol de la lista, usa `null`.\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve exclusivamente un array de objetos:\n", + "[\n", + " {\n", + " \"canonical_text\": \"Nombre Completo Normalizado\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9bed0ca", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_5 = \"\"\"\n", + "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", + "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", + "4. **Limpiar:** En `canonical_text` y `aliases`, elimina otras palabras que no sean nombres propios.\n", + "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ac77829", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_6 = \"\"\"\n", + "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", + "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", + "4. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", + "5. No modifiques los `canonical_text` ni los `aliases` originales.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26f1b81b", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_7 = \"\"\"\n", + "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", + "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", + "4. **Manejo de Siglas e Iniciales:** Ten en cuenta que las personas pueden ser mencionadas por sus iniciales (ej: \"M.L.\" para \"Martín López\"). Si el contexto permite confirmar que unas iniciales refieren a una persona ya identificada, trátalas como un alias de esa misma entidad.\n", + "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona (ya sea por nombre completo, apellido solo o iniciales), devuélvelas como una sola entidad unificada.\n", + "6. **Integridad:** No modifiques los `canonical_text` ni los `aliases` originales; mantén la literalidad de lo extraído por el NER.\n", + "\n", + "Devuelve exclusivamente un array de objetos:\n", + "[\n", + " {\n", + " \"canonical_text\": \"Nombre Completo Normalizado\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3eec132", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template_2 = \"\"\"\n", + "A continuación se proporcionan fragmentos relevantes de un documento judicial y una lista de entidades (personas) pre-clusterizadas por similitud de texto.\n", + "\n", + "# Contexto de la causa, párrafos con alguna mención a las personas:\n", + "{document_text}\n", + "\n", + "# Entidades Pre-clusterizadas a validar:\n", + "{canonical_entities}\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e07fffe7", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompts = [\n", + " system_prompt_1,\n", + " system_prompt_2,\n", + " system_prompt_3,\n", + " system_prompt_4,\n", + " system_prompt_5,\n", + " system_prompt_6,\n", + " system_prompt_7,\n", + "]\n", + "\n", + "user_prompt_templates = [\n", + " user_prompt_template_1,\n", + " user_prompt_template_2,\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82835596", + "metadata": {}, + "outputs": [], + "source": [ + "def llm_infer_canonical_entities(\n", + " system_prompt: str,\n", + " user_prompt_template: str,\n", + " model: str,\n", + " document_path: Path,\n", + " pre_cluster_path: Path,\n", + " gold_path: Path,\n", + " context_window_length: int = 120,\n", + " model_context: int = 9_500,\n", + " target_label: str = \"PER\"\n", + ") -> dict:\n", + " \n", + " \"\"\"\n", + " Infers canonical entities using an LLM by providing context windows \n", + " around detected PER entities and pre-clusterization results.\n", + " \"\"\"\n", + "\n", + " # 1. Filename cleaning\n", + " clean_base_name = document_path.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", + " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + "\n", + " print(f\"Processing document: {target_filename}\")\n", + "\n", + " # 2. Extract document text via API\n", + " session = requests.Session()\n", + " api_response = call_extraction_api(session, Path(document_path))\n", + " document_paragraphs = api_response.get(\"detail\", {}).get(\"document\")\n", + "\n", + " if not document_paragraphs:\n", + " raise ValueError(\"Document text is empty or not found for {target_filename}.\")\n", + "\n", + " # 3. Extract context windows for target label\n", + " context_windows = set()\n", + "\n", + " for paragraph in tqdm(document_paragraphs, desc=\"Extracting context\"):\n", + " preds = get_predictions(paragraph)\n", + " doc_text = preds['document']\n", + " \n", + " for label in preds['labels']:\n", + " label_type = label['attrs'].get('aymurai_label')\n", + " \n", + " # Filter: only PER labels\n", + " if label_type == target_label:\n", + " start = label['start_char']\n", + " end = label['end_char']\n", + " \n", + " # Define window boundaries\n", + " window_start = max(0, start - context_window_length)\n", + " window_end = min(len(doc_text), end + context_window_length)\n", + "\n", + " # Extract and clean the snippet\n", + " snippet = \" \".join(doc_text[window_start:window_end].split())\n", + " context_windows.add(snippet)\n", + "\n", + " context_windows = list(context_windows)\n", + " \n", + " # 4. Prepare Canonical Entities from Pre-cluster\n", + " canonical_entities_pre_cluster = load_json(json_file_path=pre_cluster_path)\n", + "\n", + " # Clean entities to save tokens\n", + " canonical_entities_prompt = [\n", + " {\n", + " k: v\n", + " for k, v in ce.items()\n", + " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", + " }\n", + " for ce in canonical_entities_pre_cluster\n", + " ]\n", + "\n", + " # 5. Build Final User Prompt\n", + " user_prompt = user_prompt_template.format(\n", + " document_text=\"\\n\".join(context_windows).strip(),\n", + " canonical_entities=get_pretty(canonical_entities_prompt),\n", + " )\n", + "\n", + " # 6. LLM Inference\n", + " provider = OllamaLLMProvider(model=model)\n", + " response = provider.generate(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options={\"temperature\": 0, \"num_ctx\": model_context},\n", + " format=CanonicalEntities.model_json_schema(),\n", + " )\n", + "\n", + " # 7. Parse and Validate Output\n", + " raw_llm_output = [\n", + " output for output in json.loads(response.text)[\"canonical_entities\"]\n", + " ]\n", + "\n", + " canonical_entities_llm = [\n", + " CanonicalEntity.model_validate(canonical_entity)\n", + " for canonical_entity in raw_llm_output\n", + " ]\n", + "\n", + " # 8. Load Gold Standard for reference\n", + " canonical_entities_gold = load_json(json_file_path=gold_path)\n", + "\n", + " print(f\"\\nSummary for {target_filename}:\")\n", + " print(f\"- Pre-cluster entities: {len(canonical_entities_pre_cluster)}\")\n", + " print(f\"- LLM generated entities: {len(canonical_entities_llm)}\")\n", + " print(f\"- Gold standard entities: {len(canonical_entities_gold)}\")\n", + "\n", + " # Return structured results\n", + " return {\n", + " \"pre_cluster_json\": canonical_entities_pre_cluster,\n", + " \"llm_output_json\": canonical_entities_llm,\n", + " \"gold_standard_json\": canonical_entities_gold,\n", + " \"system_prompt\": system_prompt,\n", + " \"user_prompt\": user_prompt\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586b1ddd", + "metadata": {}, + "outputs": [], + "source": [ + "def get_document_paths(document_check: Path, pre_clusterization_root: Path) -> dict:\n", + " \"\"\"\n", + " Generates the pre-cluster and gold JSON paths for a given document.\n", + " \n", + " Args:\n", + " document_check (Path): The original document Path object.\n", + " pre_clusterization_root (Path): The root directory for pre-clusterization data.\n", + " \n", + " Returns:\n", + " dict: A dictionary containing the 'pre_cluster_path' and 'gold_path'.\n", + " \"\"\"\n", + " # 1. Prepare target filename\n", + " clean_base_name = document_check.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", + " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + "\n", + " # 2. Define subdirectories\n", + " pre_cluster_dir = pre_clusterization_root / 'best-pre-clusterization-per'\n", + " gold_dir = pre_clusterization_root / 'gold-jsons'\n", + "\n", + " # 3. Build final paths\n", + " pre_cluster_json_path = pre_cluster_dir / f\"{target_filename}-ner-preds-pre-cluster.json\"\n", + " gold_json_path = gold_dir / f\"{target_filename}-canonical-entities-gold.json\"\n", + "\n", + " return {\n", + " \"target_filename\": target_filename,\n", + " \"pre_cluster_path\": pre_cluster_json_path,\n", + " \"gold_path\": gold_json_path\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c2163a5", + "metadata": {}, + "outputs": [], + "source": [ + "document_check = documents[0]\n", + "PRE_CLUSTERIZATION_ROOT = GOLD_JSON_ROOT.parent / \"pre-clusterization\"\n", + "paths = get_document_paths(document_check, PRE_CLUSTERIZATION_ROOT)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42211950", + "metadata": {}, + "outputs": [], + "source": [ + "context_window_lengths = list(range(80, 200, 10))\n", + "\n", + "model_context_lengths = [7_500, 8_000, 8_500, 9_000, 9_500]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5f542f5", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict = llm_infer_canonical_entities(\n", + " system_prompt=system_prompts['system_prompt_7'],\n", + " user_prompt_template=user_prompt_templates['user_prompt_template_2'],\n", + " model=models[0],\n", + " document_path=document_check,\n", + " pre_cluster_path=paths[\"pre_cluster_path\"],\n", + " gold_path=paths[\"gold_path\"],\n", + " context_window_length=120,\n", + " model_context=9_500,\n", + " target_label=\"PER\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a029e62", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict['llm_output_json']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "027b77cc", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict['pre_cluster_json']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1087e778", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict['gold_standard_json']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c28436f", + "metadata": {}, + "outputs": [], + "source": [ + "print(llm_response_dict['user_prompt'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b51fc151", + "metadata": {}, + "outputs": [], + "source": [ + "print(llm_response_dict['system_prompt'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90229978", + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "\n", + "def get_name(obj):\n", + " \"\"\"Helper to extract a clean string name from a function or object.\"\"\"\n", + " if obj is None:\n", + " return \"None\"\n", + " if hasattr(obj, \"__name__\"):\n", + " return obj.__name__\n", + " # Fallback for complex objects or partials: remove memory addresses\n", + " clean_name = re.sub(r\" at 0x[0-9a-fA-F]+\", \"\", str(obj))\n", + " return clean_name.strip(\"<>\").replace(\"cyfunction \", \"\").replace(\"function \", \"\")\n", + "\n", + "\n", + "def run_llm_grid_search(\n", + " models: list,\n", + " system_prompts: dict,\n", + " user_prompt_templates: dict,\n", + " context_window_lengths: list,\n", + " model_context_lengths: list,\n", + " documents: list[Path],\n", + " target_label: str,\n", + " base_output_path: Path,\n", + "):\n", + " \"\"\"\n", + " Runs a grid search over hyperparameters using nested loops.\n", + " \"\"\"\n", + "\n", + " # To keep track of all results and find the best\n", + " all_combinations_results = []\n", + "\n", + " for model in models:\n", + " for system_prompt_name, system_prompt in system_prompts.items():\n", + " for user_prompt_template_name, user_prompt_template in user_prompt_templates.items():\n", + " for context_window_length in context_window_lengths:\n", + " for model_context in model_context_lengths:\n", + " \n", + " print(\n", + " f\"\\nRunning grid search with model: {model}, \"\n", + " f\"context_window_length: {context_window_length}, \"\n", + " f\"model_context: {model_context}\"\n", + " )\n", + "\n", + " # 1. Create a descriptive folder name with the cleaned names\n", + " combo_name = (\n", + " f\"model-{model}-system_prompt-{system_prompt_name}-\"\n", + " f\"user_prompt_template-{user_prompt_template_name}-context_window-{context_window_length}-\"\n", + " f\"model_context-{model_context}\"\n", + " )\n", + "\n", + " combo_dir = base_output_path / combo_name\n", + " combo_dir.mkdir(parents=True, exist_ok=True)\n", + "\n", + " metrics_results = {}\n", + " metrics_file_path = (\n", + " combo_dir / f\"evaluation_metrics_{target_label}.json\"\n", + " )\n", + "\n", + " # List to collect scores for this specific combination\n", + " current_combo_scores = []\n", + "\n", + " for document in documents:\n", + " \n", + " # Get document paths\n", + " paths = get_document_paths(document, PRE_CLUSTERIZATION_ROOT)\n", + "\n", + " # Run LLM inference\n", + " llm_response_dict = llm_infer_canonical_entities(\n", + " system_prompt=system_prompt,\n", + " user_prompt_template=user_prompt_template,\n", + " model=model,\n", + " document_path=document,\n", + " pre_cluster_path=paths['pre_cluster_path'],\n", + " gold_path=paths['gold_path'],\n", + " context_window_length=context_window_length,\n", + " model_context=model_context,\n", + " target_label=target_label\n", + " )\n", + " # Extract predictions\n", + " ce_llm = llm_response_dict['llm_output_json']\n", + " \n", + " # Validate and prepare for saving\n", + " ce_llm = [\n", + " CanonicalEntity.model_validate(entity)\n", + " for entity in ce_llm\n", + " ]\n", + "\n", + " ce_llm = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in ce_llm\n", + " ]\n", + "\n", + " # Save the result as the llm-pred.json\n", + " target_filename = paths['target_filename']\n", + " llm_filename = f\"{target_filename}-llm-pred.json\"\n", + " llm_output_path = combo_dir / llm_filename\n", + "\n", + " save_json(\n", + " file_path=llm_output_path, json_data=ce_llm\n", + " )\n", + "\n", + " # Extract gold standard\n", + " ce_gold = llm_response_dict['gold_standard_json']\n", + " \n", + " # Validate and prepare for saving\n", + " ce_gold = [\n", + " CanonicalEntity.model_validate(entity)\n", + " for entity in ce_gold\n", + " ]\n", + "\n", + " ce_gold = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in ce_gold\n", + " ]\n", + "\n", + " len_llm = len(ce_llm)\n", + " len_gold = len(ce_gold)\n", + "\n", + " # Evaluate metrics\n", + " score, metrics = aymurai_metrics.evaluate_disambiguation(\n", + " gold_json=ce_gold,\n", + " pred_json=ce_llm,\n", + " target_label=target_label,\n", + " )\n", + " current_combo_scores.append(score)\n", + " doc_id = target_filename\n", + " metrics_results[doc_id] = {\n", + " \"label\": target_label,\n", + " \"metric_value\": score,\n", + " \"detailed_metrics\": metrics,\n", + " \"len_llm_predictions\": len_llm,\n", + " \"len_gold_standard\": len_gold\n", + " }\n", + "\n", + " # --- Calculate Average for this combination ---\n", + " avg_score = (\n", + " np.mean(current_combo_scores) if current_combo_scores else 0.0\n", + " )\n", + "\n", + " # Add average to the results file for this folder\n", + " final_output = {\n", + " \"average_combination_score\": avg_score,\n", + " \"document_details\": metrics_results,\n", + " }\n", + "\n", + " with open(metrics_file_path, \"w\") as f:\n", + " json.dump(final_output, f, indent=4)\n", + "\n", + " # Store combination info for final ranking\n", + " all_combinations_results.append(\n", + " {\n", + " \"name\": combo_name,\n", + " \"score\": avg_score,\n", + " \"params\": {\n", + " \"model\": model,\n", + " \"system_prompt\": system_prompt_name,\n", + " \"user_prompt_template\": user_prompt_template_name,\n", + " \"context_window_length\": context_window_length,\n", + " \"model_context\": model_context\n", + " },\n", + " }\n", + " )\n", + "\n", + " # Save all combinations results in a summary file\n", + " summary_file_path = (\n", + " base_output_path / f\"results_summary_{target_label}.json\"\n", + " )\n", + " with open(summary_file_path, \"w\") as f:\n", + " json.dump(all_combinations_results, f, indent=4)\n", + "\n", + " # --- Find the best combination ---\n", + " best_combo = max(all_combinations_results, key=lambda x: x[\"score\"])\n", + "\n", + " print(f\"\\nGrid Search for {target_label} completed.\")\n", + "\n", + " # We now copy the best combination folder to a new location with a standardized name\n", + " src = Path(base_output_path) / best_combo[\"name\"]\n", + " # Combine the parent destination path with the new folder name\n", + " new_name = f\"best-llm-pred-{target_label.lower()}\"\n", + " dest = Path(base_output_path.parent) / new_name\n", + " \n", + " try:\n", + " # copytree creates the destination directory with the 'new_name'\n", + " shutil.copytree(src, dest)\n", + " print(f\"Successfully copied '{src.name}' to '{dest}'\")\n", + " except FileExistsError:\n", + " print(f\"Error: A folder named '{new_name}' already exists in '{base_output_path.parent}'\")\n", + " except Exception as e:\n", + " print(f\"An error occurred: {e}\") \n", + "\n", + " return best_combo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5110dc60", + "metadata": {}, + "outputs": [], + "source": [ + "models = [\n", + " \"phi4:14b\",\n", + " \"gpt-oss:20b\",\n", + " \"llama3\",\n", + " \"llama3.1:8b\",\n", + " \"gemma3:270m\",\n", + "]\n", + "\n", + "system_prompts = {\n", + " \"system_prompt_1\": system_prompt_1,\n", + " \"system_prompt_2\": system_prompt_2,\n", + " \"system_prompt_3\": system_prompt_3,\n", + " \"system_prompt_4\": system_prompt_4,\n", + " \"system_prompt_5\": system_prompt_5,\n", + " \"system_prompt_6\": system_prompt_6,\n", + " \"system_prompt_7\": system_prompt_7\n", + "}\n", + "\n", + "\n", + "user_prompt_templates = {\n", + " \"user_prompt_template_1\": user_prompt_template_1,\n", + " \"user_prompt_template_2\": user_prompt_template_2,\n", + "}\n", + "\n", + "context_window_lengths = list(range(80, 200, 10))\n", + "\n", + "model_context_lengths = [7_500, 8_000, 8_500, 9_000, 9_500]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fe27195", + "metadata": {}, + "outputs": [], + "source": [ + "models = [\n", + " \"phi4:14b\",\n", + "]\n", + "\n", + "system_prompts = {\n", + " \"system_prompt_7\": system_prompt_7\n", + "}\n", + "\n", + "\n", + "user_prompt_templates = {\n", + " \"user_prompt_template_2\": user_prompt_template_2,\n", + "}\n", + "\n", + "context_window_lengths = [120]\n", + "\n", + "model_context_lengths = [9_500]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a32b4244", + "metadata": {}, + "outputs": [], + "source": [ + "top_result = run_llm_grid_search(\n", + " models=models,\n", + " system_prompts=system_prompts,\n", + " user_prompt_templates=user_prompt_templates,\n", + " context_window_lengths=context_window_lengths,\n", + " model_context_lengths=model_context_lengths,\n", + " documents=documents[0:3],\n", + " target_label=\"PER\",\n", + " base_output_path=GOLD_JSON_ROOT.parent / \"llm-grid-search-results\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86da6a45", + "metadata": {}, + "outputs": [], + "source": [ + "print(\n", + " f\"The best hyperparameter combination is '{top_result['name']}' \"\n", + " f\"with an average score of {top_result['score']:.4f}.\"\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb new file mode 100644 index 00000000..61c88f82 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb @@ -0,0 +1,185 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b3a62938", + "metadata": {}, + "source": [ + "# Disambiguation endpoint smoke test\n", + "\n", + "This notebook runs the full flow using the API endpoints:\n", + "1) document extraction\n", + "2) NER prediction per paragraph\n", + "3) disambiguation\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e468613", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import os\n", + "from pathlib import Path\n", + "\n", + "import requests\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a54d0295", + "metadata": {}, + "outputs": [], + "source": [ + "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8999\")\n", + "DATA_ROOT = Path(\n", + " os.getenv(\n", + " \"DISAMBIGUATION_DATA_ROOT\",\n", + " \"/resources/data/restricted/disambiguation-eval/documents\",\n", + " )\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "\n", + "LIMIT = int(os.getenv(\"DISAMBIGUATION_DOC_LIMIT\", \"0\")) # 0 = no limit\n", + "TARGET_LABELS = os.getenv(\"DISAMBIGUATION_TARGET_LABELS\", \"PER\")\n", + "TARGET_LABELS = [label.strip() for label in TARGET_LABELS.split(\",\") if label.strip()]\n", + "\n", + "FUZZY_THRESHOLD = int(os.getenv(\"DISAMBIGUATION_THRESHOLD\", \"70\"))\n", + "FUZZY_SCORER = os.getenv(\"DISAMBIGUATION_SCORER\", \"token_set_ratio\")\n", + "FUZZY_PROCESSOR = os.getenv(\"DISAMBIGUATION_PROCESSOR\", \"light_normalizer\")\n", + "\n", + "print(f\"API: {API_BASE_URL}\")\n", + "print(f\"Data root: {DATA_ROOT}\")\n", + "print(f\"Target labels: {TARGET_LABELS or 'ALL'}\")\n", + "print(\n", + " f\"Fuzzy params: scorer={FUZZY_SCORER}, threshold={FUZZY_THRESHOLD}, processor={FUZZY_PROCESSOR}\"\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d3d5787", + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.experiments.entity_disambiguation.runner import (\n", + " call_extraction_api as extract_document,\n", + ")\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: set[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "def predict_paragraphs(paragraphs: list[str]) -> list[dict]:\n", + " endpoint = f\"{API_BASE_URL}/anonymizer/predict\"\n", + " predictions = []\n", + " for paragraph in paragraphs:\n", + " response = requests.post(endpoint, json={\"text\": paragraph}, timeout=60)\n", + " response.raise_for_status()\n", + " predictions.append(response.json())\n", + " return predictions\n", + "\n", + "\n", + "def disambiguate(predictions: list[dict]) -> dict:\n", + " endpoint = f\"{API_BASE_URL}/anonymizer/disambiguate\"\n", + " params = {\n", + " \"threshold\": FUZZY_THRESHOLD,\n", + " \"scorer\": FUZZY_SCORER,\n", + " \"processor\": FUZZY_PROCESSOR,\n", + " \"target_labels\": TARGET_LABELS,\n", + " }\n", + " if TARGET_LABELS:\n", + " params[\"target_labels\"] = TARGET_LABELS\n", + " response = requests.post(endpoint, params=params, json=predictions, timeout=120)\n", + " response.raise_for_status()\n", + " return response.json()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72f3d427", + "metadata": {}, + "outputs": [], + "source": [ + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "if LIMIT > 0:\n", + " documents = documents[:LIMIT]\n", + "\n", + "print(f\"Found {len(documents)} documents\")\n", + "documents[:5]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1eb1c141", + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.experiments.entity_disambiguation.runner import call_extraction_api\n", + "\n", + "\n", + "for doc_path in documents:\n", + " print(f\"\\n=== {doc_path.name} ===\")\n", + " session = requests.Session()\n", + " document = extract_document(\n", + " session,\n", + " endpoint=f\"{API_BASE_URL}/misc/document-extract\",\n", + " file_path=doc_path,\n", + " timeout_s=300,\n", + " )\n", + " paragraphs = document[\"detail\"][\"document\"]\n", + " print(f\"Paragraphs: {len(paragraphs)}\")\n", + "\n", + " predictions = predict_paragraphs(paragraphs)\n", + " print(f\"Predictions: {len(predictions)}\")\n", + "\n", + " disambiguated = disambiguate(predictions)\n", + " print(f\"Canonical entities: {len(disambiguated.get('canonical_entities', []))}\")\n", + " print(json.dumps(disambiguated, indent=2, ensure_ascii=False))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "877f5167", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai (3.10.19)", + "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.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/uv.lock b/uv.lock index be85f765..9913d119 100644 --- a/uv.lock +++ b/uv.lock @@ -1,18 +1,19 @@ version = 1 +revision = 3 requires-python = "==3.10.*" resolution-markers = [ - "platform_system == 'Darwin'", - "platform_machine == 'aarch64' and platform_system == 'Linux'", - "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')", + "sys_platform == 'darwin'", + "platform_machine == 'aarch64' and sys_platform == 'linux'", + "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", ] [[package]] name = "absl-py" version = "2.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588 } +sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811 }, + { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" }, ] [[package]] @@ -37,9 +38,9 @@ wheels = [ name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] [[package]] @@ -85,9 +86,9 @@ dependencies = [ { name = "frozenlist" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] [[package]] @@ -100,9 +101,9 @@ dependencies = [ { name = "tomli" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064 } +sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064, upload-time = "2025-11-14T20:35:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554 }, + { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554, upload-time = "2025-11-14T20:35:05.699Z" }, ] [[package]] @@ -125,18 +126,18 @@ wheels = [ name = "annotated-doc" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] @@ -152,9 +153,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191, upload-time = "2025-02-18T20:35:33.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228 }, + { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228, upload-time = "2025-02-18T20:35:28.659Z" }, ] [[package]] @@ -175,9 +176,9 @@ wheels = [ name = "appnope" version = "0.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, ] [[package]] @@ -187,9 +188,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, ] [[package]] @@ -199,23 +200,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, - { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549 }, - { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539 }, - { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467 }, - { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355 }, - { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, + { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549, upload-time = "2025-07-30T10:02:00.101Z" }, + { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539, upload-time = "2025-07-30T10:02:00.929Z" }, + { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467, upload-time = "2025-07-30T10:02:02.08Z" }, + { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355, upload-time = "2025-07-30T10:02:02.867Z" }, + { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187, upload-time = "2025-07-30T10:02:03.674Z" }, ] [[package]] @@ -226,18 +227,18 @@ dependencies = [ { name = "python-dateutil" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, ] [[package]] name = "asttokens" version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] [[package]] @@ -247,18 +248,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380 } +sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380, upload-time = "2025-03-16T17:25:36.919Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069 }, + { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069, upload-time = "2025-03-16T17:25:35.422Z" }, ] [[package]] name = "async-timeout" version = "5.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, ] [[package]] @@ -268,18 +269,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217, upload-time = "2024-08-24T23:15:36.449Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209 }, + { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209, upload-time = "2024-08-24T23:15:35.317Z" }, ] [[package]] name = "attrs" version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] [[package]] @@ -411,9 +412,9 @@ mlops = [ name = "babel" version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, ] [[package]] @@ -424,9 +425,9 @@ dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] [[package]] @@ -440,9 +441,9 @@ dependencies = [ { name = "lxml" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924 } +sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924, upload-time = "2023-08-15T22:34:59.047Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419 }, + { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419, upload-time = "2023-08-15T22:34:56.022Z" }, ] [[package]] @@ -452,9 +453,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533, upload-time = "2025-10-27T17:57:39.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437, upload-time = "2025-10-27T17:57:37.538Z" }, ] [package.optional-dependencies] @@ -466,9 +467,9 @@ css = [ name = "blinker" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, ] [[package]] @@ -524,54 +525,54 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283 }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504 }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811 }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402 }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217 }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079 }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475 }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829 }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211 }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036 }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184 }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790 }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, ] [[package]] name = "cfgv" version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 }, - { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 }, - { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 }, - { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 }, - { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 }, - { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 }, - { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 }, - { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 }, - { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 }, - { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 }, - { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 }, - { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 }, - { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 }, - { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 }, - { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 }, - { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] [[package]] @@ -579,29 +580,29 @@ name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] name = "cloudpickle" version = "3.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -609,29 +610,29 @@ name = "colorlog" version = "6.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743 }, + { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, ] [[package]] name = "comm" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, ] [[package]] name = "conllu" version = "4.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768 } +sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768, upload-time = "2023-06-19T12:37:49.632Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098 }, + { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098, upload-time = "2023-06-19T12:37:47.885Z" }, ] [[package]] @@ -641,7 +642,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551 }, { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399 }, @@ -706,7 +707,7 @@ wheels = [ name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, ] @@ -772,18 +773,18 @@ wheels = [ name = "decorator" version = "5.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] [[package]] name = "defusedxml" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, ] [[package]] @@ -793,52 +794,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] [[package]] name = "dill" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976 } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668 }, + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, ] [[package]] name = "diskcache" version = "5.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] [[package]] name = "distro" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] [[package]] name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, ] @@ -861,15 +862,15 @@ wheels = [ name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } [[package]] name = "docx2txt" version = "0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613, upload-time = "2025-03-24T20:59:25.21Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025 }, + { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025, upload-time = "2025-03-24T20:59:24.394Z" }, ] [[package]] @@ -905,9 +906,9 @@ wheels = [ name = "einops" version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805, upload-time = "2025-02-09T03:17:00.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359, upload-time = "2025-02-09T03:17:01.998Z" }, ] [[package]] @@ -918,9 +919,9 @@ dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] [[package]] @@ -930,18 +931,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] [[package]] name = "executing" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] [[package]] @@ -951,9 +952,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644, upload-time = "2023-06-27T15:24:28.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039 }, + { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039, upload-time = "2023-06-27T15:24:24.869Z" }, ] [[package]] @@ -1027,62 +1028,62 @@ wheels = [ name = "fastar" version = "0.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536 }, - { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235 }, - { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386 }, - { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955 }, - { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987 }, - { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900 }, - { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523 }, - { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268 }, - { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286 }, - { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921 }, - { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302 }, - { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141 }, - { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544 }, - { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701 }, - { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544 }, - { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020 }, - { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735 }, - { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779 }, - { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755 }, - { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732 }, - { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571 }, - { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440 }, - { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424 }, - { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675 }, - { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098 }, - { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397 }, +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536, upload-time = "2025-11-26T02:34:35.236Z" }, + { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235, upload-time = "2025-11-26T02:34:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386, upload-time = "2025-11-26T02:33:47.613Z" }, + { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955, upload-time = "2025-11-26T02:32:44.279Z" }, + { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987, upload-time = "2025-11-26T02:32:59.701Z" }, + { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900, upload-time = "2025-11-26T02:33:16.059Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523, upload-time = "2025-11-26T02:33:30.897Z" }, + { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268, upload-time = "2025-11-26T02:34:04.003Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286, upload-time = "2025-11-26T02:34:50.279Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921, upload-time = "2025-11-26T02:35:07.292Z" }, + { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302, upload-time = "2025-11-26T02:35:24.995Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141, upload-time = "2025-11-26T02:35:42.449Z" }, + { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544, upload-time = "2025-11-26T02:36:23.801Z" }, + { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701, upload-time = "2025-11-26T02:36:09.625Z" }, + { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544, upload-time = "2025-11-26T02:34:46.195Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020, upload-time = "2025-11-26T02:34:31.085Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735, upload-time = "2025-11-26T02:34:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779, upload-time = "2025-11-26T02:32:55.109Z" }, + { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755, upload-time = "2025-11-26T02:33:11.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732, upload-time = "2025-11-26T02:33:27.122Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571, upload-time = "2025-11-26T02:33:42.986Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440, upload-time = "2025-11-26T02:34:15.439Z" }, + { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424, upload-time = "2025-11-26T02:35:02.742Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675, upload-time = "2025-11-26T02:35:20.252Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098, upload-time = "2025-11-26T02:35:37.643Z" }, + { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397, upload-time = "2025-11-26T02:35:56.215Z" }, ] [[package]] name = "fastjsonschema" version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, ] [[package]] name = "fastuuid" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760 }, - { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748 }, - { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537 }, - { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994 }, - { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003 }, - { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583 }, - { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955 }, - { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763 }, - { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613 }, - { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045 }, - { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122 }, + { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760, upload-time = "2025-10-19T22:25:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748, upload-time = "2025-10-19T22:41:52.873Z" }, + { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537, upload-time = "2025-10-19T22:33:55.603Z" }, + { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994, upload-time = "2025-10-19T22:26:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003, upload-time = "2025-10-19T22:23:45.415Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583, upload-time = "2025-10-19T22:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955, upload-time = "2025-10-19T22:36:25.196Z" }, + { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763, upload-time = "2025-10-19T22:24:28.451Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613, upload-time = "2025-10-19T22:25:06.827Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045, upload-time = "2025-10-19T22:28:32.732Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122, upload-time = "2025-10-19T22:23:15.59Z" }, ] [[package]] @@ -1098,9 +1099,9 @@ wheels = [ name = "filetype" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020 } +sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970 }, + { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, ] [[package]] @@ -1110,9 +1111,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "termcolor" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720, upload-time = "2025-08-16T20:20:24.175Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945 }, + { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945, upload-time = "2025-08-16T20:20:22.87Z" }, ] [[package]] @@ -1146,7 +1147,7 @@ dependencies = [ { name = "transformers", extra = ["sentencepiece"] }, { name = "wikipedia-api" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607 } +sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607, upload-time = "2025-02-05T14:45:44.322Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, ] @@ -1202,43 +1203,43 @@ wheels = [ name = "fqdn" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, ] [[package]] name = "frozenlist" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230 }, - { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621 }, - { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889 }, - { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464 }, - { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649 }, - { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188 }, - { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748 }, - { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351 }, - { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767 }, - { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887 }, - { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785 }, - { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312 }, - { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650 }, - { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659 }, - { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837 }, - { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989 }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] [[package]] name = "fsspec" version = "2025.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285 } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966 }, + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, ] [package.optional-dependencies] @@ -1253,9 +1254,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, ] [[package]] @@ -1289,9 +1290,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smmap" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, ] [[package]] @@ -1317,9 +1318,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759 } +sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759, upload-time = "2025-10-28T21:34:51.529Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706 }, + { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706, upload-time = "2025-10-28T21:34:50.151Z" }, ] [[package]] @@ -1348,9 +1349,9 @@ dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027, upload-time = "2025-10-29T23:17:39.513Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469 }, + { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469, upload-time = "2025-10-29T23:17:38.548Z" }, ] [[package]] @@ -1411,9 +1412,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-crc32c" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265 } +sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265, upload-time = "2025-11-17T15:38:06.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340 }, + { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340, upload-time = "2025-11-17T15:38:05.594Z" }, ] [[package]] @@ -1468,7 +1469,7 @@ wheels = [ name = "greenlet" version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658 }, { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810 }, @@ -1496,24 +1497,24 @@ wheels = [ name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "hf-xet" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099 }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178 }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214 }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054 }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812 }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920 }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, ] [[package]] @@ -1524,24 +1525,24 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] name = "httptools" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531 }, - { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408 }, - { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889 }, - { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460 }, - { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267 }, - { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429 }, - { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173 }, + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" }, ] [[package]] @@ -1554,7 +1555,7 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] @@ -1582,27 +1583,27 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358 } +sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358, upload-time = "2025-10-23T12:12:01.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094 }, + { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094, upload-time = "2025-10-23T12:11:59.557Z" }, ] [[package]] name = "identify" version = "2.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183 }, + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] @@ -1634,7 +1635,7 @@ name = "ipykernel" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "platform_system == 'Darwin'" }, + { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, { name = "ipython" }, @@ -1648,9 +1649,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579, upload-time = "2025-10-27T09:46:39.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968 }, + { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968, upload-time = "2025-10-27T09:46:37.805Z" }, ] [[package]] @@ -1658,7 +1659,7 @@ name = "ipython" version = "8.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "decorator" }, { name = "exceptiongroup" }, { name = "jedi" }, @@ -1686,9 +1687,9 @@ dependencies = [ { name = "traitlets" }, { name = "widgetsnbextension" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, ] [[package]] @@ -1698,7 +1699,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "arrow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, ] @@ -1719,9 +1720,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parso" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, ] [[package]] @@ -1731,37 +1732,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "jiter" version = "0.12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652 }, - { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829 }, - { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568 }, - { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052 }, - { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585 }, - { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541 }, - { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423 }, - { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958 }, - { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084 }, - { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054 }, - { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368 }, - { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847 }, - { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144 }, - { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877 }, - { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419 }, - { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212 }, - { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974 }, - { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233 }, - { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537 }, - { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110 }, +sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652, upload-time = "2025-11-09T20:46:41.021Z" }, + { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829, upload-time = "2025-11-09T20:46:43.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568, upload-time = "2025-11-09T20:46:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052, upload-time = "2025-11-09T20:46:46.818Z" }, + { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585, upload-time = "2025-11-09T20:46:48.319Z" }, + { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541, upload-time = "2025-11-09T20:46:49.643Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423, upload-time = "2025-11-09T20:46:51.731Z" }, + { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958, upload-time = "2025-11-09T20:46:53.432Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084, upload-time = "2025-11-09T20:46:54.848Z" }, + { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054, upload-time = "2025-11-09T20:46:56.487Z" }, + { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368, upload-time = "2025-11-09T20:46:58.638Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847, upload-time = "2025-11-09T20:47:00.295Z" }, ] [[package]] @@ -1772,18 +1765,18 @@ dependencies = [ { name = "click" }, { name = "rapidfuzz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537 } +sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537, upload-time = "2024-11-01T16:18:57.337Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990, upload-time = "2024-11-01T16:18:55.928Z" }, ] [[package]] name = "jmespath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 } +sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 }, + { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, ] [[package]] @@ -1820,18 +1813,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359 } +sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359, upload-time = "2023-09-01T12:34:44.187Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701 }, + { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701, upload-time = "2023-09-01T12:34:42.563Z" }, ] [[package]] name = "jsonpointer" version = "3.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, ] [[package]] @@ -1869,9 +1862,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] [[package]] @@ -1886,9 +1879,9 @@ dependencies = [ { name = "nbconvert" }, { name = "notebook" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, ] [[package]] @@ -1921,9 +1914,9 @@ dependencies = [ { name = "pyzmq" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, ] [[package]] @@ -1934,9 +1927,9 @@ dependencies = [ { name = "platformdirs" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, ] [[package]] @@ -1953,9 +1946,9 @@ dependencies = [ { name = "rfc3986-validator" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430 }, + { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, ] [[package]] @@ -1965,9 +1958,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823, upload-time = "2025-08-27T17:47:34.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687 }, + { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687, upload-time = "2025-08-27T17:47:33.15Z" }, ] [[package]] @@ -1987,7 +1980,7 @@ dependencies = [ { name = "overrides" }, { name = "packaging" }, { name = "prometheus-client" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "pyzmq" }, { name = "send2trash" }, { name = "terminado" }, @@ -1995,9 +1988,9 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949, upload-time = "2025-08-21T14:42:54.042Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221 }, + { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221, upload-time = "2025-08-21T14:42:52.034Z" }, ] [[package]] @@ -2005,12 +1998,12 @@ name = "jupyter-server-terminals" version = "0.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "terminado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430, upload-time = "2024-03-12T14:37:03.049Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656 }, + { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656, upload-time = "2024-03-12T14:37:00.708Z" }, ] [[package]] @@ -2042,9 +2035,9 @@ wheels = [ name = "jupyterlab-pygments" version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, ] [[package]] @@ -2060,44 +2053,44 @@ dependencies = [ { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, ] [[package]] name = "jupyterlab-widgets" version = "3.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, ] [[package]] name = "kiwisolver" version = "1.4.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159 }, - { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578 }, - { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312 }, - { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458 }, - { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640 }, - { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074 }, - { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036 }, - { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310 }, - { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943 }, - { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488 }, - { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787 }, - { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730 }, - { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036 }, - { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183 }, - { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675 }, - { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277 }, - { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994 }, - { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744 }, +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" }, + { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" }, + { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" }, + { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" }, + { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" }, + { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" }, + { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" }, + { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" }, + { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" }, + { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" }, + { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" }, + { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" }, ] [[package]] @@ -2107,7 +2100,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474, upload-time = "2021-05-07T07:54:13.562Z" } [[package]] name = "langextract" @@ -2131,9 +2124,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029 } +sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029, upload-time = "2025-11-14T22:21:30.511Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592 }, + { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592, upload-time = "2025-11-14T22:21:29.044Z" }, ] [package.optional-dependencies] @@ -2145,9 +2138,9 @@ openai = [ name = "lark" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, ] [[package]] @@ -2157,9 +2150,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fire" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292, upload-time = "2022-09-06T16:09:06.607Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594 }, + { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594, upload-time = "2022-09-06T16:09:04.658Z" }, ] [[package]] @@ -2180,39 +2173,39 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976, upload-time = "2025-11-16T00:03:51.812Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975 }, + { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975, upload-time = "2025-11-16T00:03:49.182Z" }, ] [[package]] name = "lxml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589 }, - { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671 }, - { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961 }, - { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087 }, - { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620 }, - { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664 }, - { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397 }, - { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178 }, - { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148 }, - { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035 }, - { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111 }, - { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662 }, - { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973 }, - { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953 }, - { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695 }, - { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051 }, - { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264 }, - { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435 }, - { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913 }, - { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357 }, - { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295 }, - { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913 }, +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589, upload-time = "2025-09-22T04:00:10.51Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671, upload-time = "2025-09-22T04:00:15.411Z" }, + { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961, upload-time = "2025-09-22T04:00:17.619Z" }, + { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087, upload-time = "2025-09-22T04:00:19.868Z" }, + { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620, upload-time = "2025-09-22T04:00:21.877Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664, upload-time = "2025-09-22T04:00:23.714Z" }, + { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397, upload-time = "2025-09-22T04:00:25.544Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178, upload-time = "2025-09-22T04:00:27.602Z" }, + { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148, upload-time = "2025-09-22T04:00:29.323Z" }, + { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035, upload-time = "2025-09-22T04:00:31.061Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111, upload-time = "2025-09-22T04:00:33.11Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662, upload-time = "2025-09-22T04:00:35.237Z" }, + { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973, upload-time = "2025-09-22T04:00:37.086Z" }, + { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953, upload-time = "2025-09-22T04:00:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695, upload-time = "2025-09-22T04:00:41.402Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051, upload-time = "2025-09-22T04:00:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264, upload-time = "2025-09-22T04:04:32.892Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435, upload-time = "2025-09-22T04:04:34.907Z" }, + { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913, upload-time = "2025-09-22T04:04:37.205Z" }, + { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357, upload-time = "2025-09-22T04:04:39.322Z" }, + { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295, upload-time = "2025-09-22T04:04:41.647Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913, upload-time = "2025-09-22T04:04:43.602Z" }, ] [[package]] @@ -2222,9 +2215,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, ] [[package]] @@ -2234,18 +2227,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "markdown2" version = "2.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652 } +sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652, upload-time = "2025-07-27T16:16:24.307Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954 }, + { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954, upload-time = "2025-07-27T16:16:23.026Z" }, ] [[package]] @@ -2256,9 +2249,9 @@ dependencies = [ { name = "beautifulsoup4" }, { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816, upload-time = "2025-11-16T19:21:18.565Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724 }, + { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724, upload-time = "2025-11-16T19:21:17.622Z" }, ] [[package]] @@ -2288,28 +2281,28 @@ dependencies = [ { name = "tqdm" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302 } +sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302, upload-time = "2025-09-30T15:44:59.591Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914 }, + { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914, upload-time = "2025-09-30T15:44:58.441Z" }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631 }, - { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057 }, - { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050 }, - { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681 }, - { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705 }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524 }, - { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282 }, - { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745 }, - { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571 }, - { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056 }, - { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932 }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, ] [[package]] @@ -2347,18 +2340,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516 }, + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] @@ -2381,7 +2374,7 @@ dependencies = [ { name = "absl-py" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356 } +sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356, upload-time = "2025-04-17T08:25:02.247Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, ] @@ -2468,9 +2461,9 @@ wheels = [ name = "more-itertools" version = "10.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667 }, + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, ] [[package]] @@ -2481,18 +2474,18 @@ dependencies = [ { name = "jinja2" }, { name = "matplotlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433, upload-time = "2025-11-05T18:12:24.183Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, + { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051, upload-time = "2025-11-05T18:12:22.527Z" }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] [[package]] @@ -2502,27 +2495,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153 }, - { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993 }, - { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607 }, - { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847 }, - { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616 }, - { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333 }, - { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239 }, - { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618 }, - { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655 }, - { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245 }, - { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523 }, - { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129 }, - { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999 }, - { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711 }, - { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504 }, - { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422 }, - { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050 }, - { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153 }, - { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317 }, +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153, upload-time = "2025-10-06T14:48:26.409Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993, upload-time = "2025-10-06T14:48:28.4Z" }, + { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607, upload-time = "2025-10-06T14:48:29.581Z" }, + { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847, upload-time = "2025-10-06T14:48:32.107Z" }, + { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616, upload-time = "2025-10-06T14:48:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333, upload-time = "2025-10-06T14:48:35.9Z" }, + { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239, upload-time = "2025-10-06T14:48:37.302Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618, upload-time = "2025-10-06T14:48:38.963Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655, upload-time = "2025-10-06T14:48:40.312Z" }, + { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245, upload-time = "2025-10-06T14:48:41.848Z" }, + { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523, upload-time = "2025-10-06T14:48:43.749Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129, upload-time = "2025-10-06T14:48:45.225Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999, upload-time = "2025-10-06T14:48:46.703Z" }, + { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711, upload-time = "2025-10-06T14:48:48.146Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504, upload-time = "2025-10-06T14:48:49.447Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422, upload-time = "2025-10-06T14:48:50.789Z" }, + { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050, upload-time = "2025-10-06T14:48:51.938Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153, upload-time = "2025-10-06T14:48:53.146Z" }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, ] [[package]] @@ -2532,14 +2525,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503 } +sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503, upload-time = "2025-04-17T03:11:27.742Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083 }, - { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128 }, - { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132 }, - { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948 }, - { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636 }, - { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478 }, + { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083, upload-time = "2025-04-17T03:11:04.223Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128, upload-time = "2025-04-17T03:11:06.045Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132, upload-time = "2025-04-17T03:11:07.533Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948, upload-time = "2025-04-17T03:11:20.223Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636, upload-time = "2025-04-17T03:11:24.936Z" }, + { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z" }, ] [[package]] @@ -2586,9 +2579,9 @@ dependencies = [ { name = "pygments" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715, upload-time = "2025-01-28T09:29:14.724Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525 }, + { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525, upload-time = "2025-01-28T09:29:12.551Z" }, ] [[package]] @@ -2601,9 +2594,9 @@ dependencies = [ { name = "jupyter-core" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, ] [[package]] @@ -2613,27 +2606,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nbformat" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747 } +sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747, upload-time = "2025-11-16T17:38:55.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122 }, + { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122, upload-time = "2025-11-16T17:38:54.164Z" }, ] [[package]] name = "nest-asyncio" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, ] [[package]] name = "networkx" version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, ] [[package]] @@ -2668,25 +2661,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, ] [[package]] @@ -2694,8 +2687,7 @@ name = "nvidia-cublas-cu12" version = "12.8.4.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, ] [[package]] @@ -2703,8 +2695,7 @@ name = "nvidia-cuda-cupti-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, ] [[package]] @@ -2712,8 +2703,7 @@ name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, - { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, ] [[package]] @@ -2721,8 +2711,7 @@ name = "nvidia-cuda-runtime-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, ] [[package]] @@ -2730,11 +2719,10 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, ] [[package]] @@ -2742,11 +2730,10 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, ] [[package]] @@ -2754,8 +2741,7 @@ name = "nvidia-cufile-cu12" version = "1.13.1.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, - { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, ] [[package]] @@ -2763,8 +2749,7 @@ name = "nvidia-curand-cu12" version = "10.3.9.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, ] [[package]] @@ -2772,13 +2757,12 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, - { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, ] [[package]] @@ -2786,11 +2770,10 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, ] [[package]] @@ -2798,8 +2781,7 @@ name = "nvidia-cusparselt-cu12" version = "0.7.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, ] [[package]] @@ -2807,8 +2789,7 @@ name = "nvidia-nccl-cu12" version = "2.27.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, ] [[package]] @@ -2816,8 +2797,7 @@ name = "nvidia-nvjitlink-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, - { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, ] [[package]] @@ -2825,8 +2805,7 @@ name = "nvidia-nvshmem-cu12" version = "3.3.20" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/9d/3dd98852568fb845ec1f7902c90a22b240fe1cbabda411ccedf2fd737b7b/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b0b960da3842212758e4fa4696b94f129090b30e5122fea3c5345916545cff0", size = 124484616 }, - { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, + { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" }, ] [[package]] @@ -2834,8 +2813,7 @@ name = "nvidia-nvtx-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, ] [[package]] @@ -2845,7 +2823,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045 } +sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045, upload-time = "2020-01-18T16:55:48.852Z" } [[package]] name = "ollama" @@ -2855,9 +2833,9 @@ dependencies = [ { name = "httpx" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620, upload-time = "2025-11-13T23:02:17.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354 }, + { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354, upload-time = "2025-11-13T23:02:16.292Z" }, ] [[package]] @@ -2874,9 +2852,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133, upload-time = "2025-09-24T13:00:53.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627 }, + { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627, upload-time = "2025-09-24T13:00:50.754Z" }, ] [[package]] @@ -2886,7 +2864,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, @@ -2961,9 +2939,9 @@ dependencies = [ { name = "sqlalchemy" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444, upload-time = "2025-11-10T05:14:30.151Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708 }, + { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708, upload-time = "2025-11-10T05:14:28.6Z" }, ] [[package]] @@ -2991,18 +2969,18 @@ wheels = [ name = "overrides" version = "7.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] @@ -3030,18 +3008,18 @@ wheels = [ name = "pandocfilters" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, ] [[package]] name = "parso" version = "0.8.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668 }, + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, ] [[package]] @@ -3054,9 +3032,9 @@ dependencies = [ { name = "pydantic-settings" }, { name = "pypdfium2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968, upload-time = "2025-06-11T14:42:09.492Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693 }, + { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693, upload-time = "2025-06-11T14:42:08.157Z" }, ] [[package]] @@ -3066,53 +3044,53 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, ] [[package]] name = "pillow" version = "10.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, - { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, - { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, - { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, - { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, - { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, - { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, - { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, - { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, - { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, - { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, - { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, - { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, - { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, - { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, - { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, - { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, - { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload-time = "2024-07-01T09:45:22.07Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload-time = "2024-07-01T09:45:25.292Z" }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload-time = "2024-07-01T09:45:27.94Z" }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload-time = "2024-07-01T09:45:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload-time = "2024-07-01T09:45:32.868Z" }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload-time = "2024-07-01T09:45:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload-time = "2024-07-01T09:45:37.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload-time = "2024-07-01T09:45:39.89Z" }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload-time = "2024-07-01T09:45:42.771Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload-time = "2024-07-01T09:45:45.176Z" }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload-time = "2024-07-01T09:45:47.274Z" }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload-time = "2024-07-01T09:48:04.815Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload-time = "2024-07-01T09:48:07.206Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload-time = "2024-07-01T09:48:09.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload-time = "2024-07-01T09:48:12.529Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload-time = "2024-07-01T09:48:14.891Z" }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload-time = "2024-07-01T09:48:17.601Z" }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload-time = "2024-07-01T09:48:20.293Z" }, ] [[package]] name = "pip" version = "25.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622 }, + { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" }, ] [[package]] name = "platformdirs" version = "4.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731 }, + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, ] [[package]] @@ -3124,14 +3102,14 @@ dependencies = [ { name = "pyee" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424 }, - { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228 }, - { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424 }, - { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122 }, - { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645 }, - { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837 }, - { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843 }, - { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959 }, + { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424, upload-time = "2025-11-11T18:39:10.175Z" }, + { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228, upload-time = "2025-11-11T18:39:13.915Z" }, + { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424, upload-time = "2025-11-11T18:39:17.093Z" }, + { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122, upload-time = "2025-11-11T18:39:20.619Z" }, + { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645, upload-time = "2025-11-11T18:39:24.005Z" }, + { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837, upload-time = "2025-11-11T18:39:27.174Z" }, + { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843, upload-time = "2025-11-11T18:39:30.851Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959, upload-time = "2025-11-11T18:39:33.998Z" }, ] [[package]] @@ -3142,16 +3120,16 @@ dependencies = [ { name = "narwhals" }, { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624 } +sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624, upload-time = "2025-11-17T18:39:24.523Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174 }, + { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174, upload-time = "2025-11-17T18:39:20.351Z" }, ] [[package]] name = "pptree" version = "3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043 } +sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043, upload-time = "2020-04-15T18:28:53.362Z" } [[package]] name = "pre-commit" @@ -3173,9 +3151,9 @@ wheels = [ name = "prometheus-client" version = "0.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481 } +sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481, upload-time = "2025-09-18T20:47:25.043Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145 }, + { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, ] [[package]] @@ -3185,33 +3163,33 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, ] [[package]] name = "propcache" version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534 }, - { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526 }, - { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263 }, - { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012 }, - { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491 }, - { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319 }, - { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856 }, - { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241 }, - { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552 }, - { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113 }, - { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778 }, - { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047 }, - { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093 }, - { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638 }, - { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229 }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] [[package]] @@ -3245,57 +3223,57 @@ wheels = [ name = "psutil" version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565 } +sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565, upload-time = "2024-10-17T21:31:45.68Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762 }, - { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777 }, - { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259 }, - { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255 }, - { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804 }, - { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386 }, - { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228 }, + { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762, upload-time = "2024-10-17T21:32:05.991Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777, upload-time = "2024-10-17T21:32:07.872Z" }, + { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259, upload-time = "2024-10-17T21:32:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255, upload-time = "2024-10-17T21:32:11.964Z" }, + { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804, upload-time = "2024-10-17T21:32:13.785Z" }, + { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386, upload-time = "2024-10-17T21:32:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228, upload-time = "2024-10-17T21:32:23.88Z" }, ] [[package]] name = "ptyprocess" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, ] [[package]] name = "pure-eval" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] [[package]] name = "pyarrow" version = "22.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151 } +sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151, upload-time = "2025-10-24T12:30:00.762Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968 }, - { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085 }, - { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613 }, - { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059 }, - { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043 }, - { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505 }, - { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641 }, + { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968, upload-time = "2025-10-24T10:03:31.21Z" }, + { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085, upload-time = "2025-10-24T10:03:38.146Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613, upload-time = "2025-10-24T10:03:46.516Z" }, + { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059, upload-time = "2025-10-24T10:03:55.353Z" }, + { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043, upload-time = "2025-10-24T10:04:05.408Z" }, + { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505, upload-time = "2025-10-24T10:04:15.786Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641, upload-time = "2025-10-24T10:04:22.57Z" }, ] [[package]] name = "pyasn1" version = "0.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, ] [[package]] @@ -3305,18 +3283,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, ] [[package]] name = "pycparser" version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] [[package]] @@ -3329,9 +3307,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [package.optional-dependencies] @@ -3346,7 +3324,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 } +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298 }, { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475 }, @@ -3401,9 +3379,9 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184 } +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880 }, + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, ] [[package]] @@ -3414,9 +3392,9 @@ dependencies = [ { name = "jinja2" }, { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, ] [[package]] @@ -3426,18 +3404,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250 } +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730 }, + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] @@ -3472,9 +3450,9 @@ wheels = [ name = "pypandoc" version = "1.16.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477, upload-time = "2025-11-13T16:30:29.608Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451 }, + { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451, upload-time = "2025-11-13T16:30:07.66Z" }, ] [[package]] @@ -3490,29 +3468,29 @@ wheels = [ name = "pypdfium2" version = "4.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239, upload-time = "2024-05-09T18:33:17.552Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254 }, - { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624 }, - { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126 }, - { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077 }, - { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431 }, - { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008 }, - { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543 }, - { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911 }, - { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430 }, - { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951 }, - { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098 }, - { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118 }, + { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254, upload-time = "2024-05-09T18:32:48.653Z" }, + { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624, upload-time = "2024-05-09T18:32:51.458Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126, upload-time = "2024-05-09T18:32:53.581Z" }, + { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077, upload-time = "2024-05-09T18:32:55.99Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431, upload-time = "2024-05-09T18:32:57.911Z" }, + { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008, upload-time = "2024-05-09T18:32:59.886Z" }, + { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543, upload-time = "2024-05-09T18:33:02.597Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911, upload-time = "2024-05-09T18:33:05.376Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430, upload-time = "2024-05-09T18:33:08.067Z" }, + { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951, upload-time = "2024-05-09T18:33:10.567Z" }, + { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098, upload-time = "2024-05-09T18:33:13.107Z" }, + { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118, upload-time = "2024-05-09T18:33:15.489Z" }, ] [[package]] name = "pysocks" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725 }, + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, ] [[package]] @@ -3522,9 +3500,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] [[package]] @@ -3535,27 +3513,27 @@ dependencies = [ { name = "lxml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987 }, + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, ] [[package]] name = "python-dotenv" version = "1.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, ] [[package]] name = "python-json-logger" version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683 } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548 }, + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, ] [[package]] @@ -3583,9 +3561,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889 } +sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889, upload-time = "2022-11-25T19:33:07.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949 }, + { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949, upload-time = "2022-11-25T19:33:06.093Z" }, ] [[package]] @@ -3596,18 +3574,18 @@ dependencies = [ { name = "numpy" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086 } +sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086, upload-time = "2021-01-09T17:35:49.131Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564 }, + { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564, upload-time = "2021-01-09T17:35:47.543Z" }, ] [[package]] name = "pytz" version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] [[package]] @@ -3615,35 +3593,35 @@ name = "pywin32" version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432 }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103 }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557 }, + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, ] [[package]] name = "pywinpty" version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669, upload-time = "2025-10-03T21:16:29.205Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330 }, + { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330, upload-time = "2025-10-03T21:20:15.656Z" }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227 }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019 }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646 }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793 }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293 }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872 }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828 }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415 }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561 }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, ] [[package]] @@ -3653,52 +3631,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850 }, - { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380 }, - { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421 }, - { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149 }, - { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070 }, - { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441 }, - { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529 }, - { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276 }, - { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208 }, - { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766 }, - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, - { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266 }, - { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206 }, - { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747 }, - { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371 }, - { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862 }, +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850, upload-time = "2025-09-08T23:07:26.274Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380, upload-time = "2025-09-08T23:07:29.78Z" }, + { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421, upload-time = "2025-09-08T23:07:31.263Z" }, + { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149, upload-time = "2025-09-08T23:07:33.17Z" }, + { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070, upload-time = "2025-09-08T23:07:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441, upload-time = "2025-09-08T23:07:37.432Z" }, + { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529, upload-time = "2025-09-08T23:07:39.047Z" }, + { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276, upload-time = "2025-09-08T23:07:40.695Z" }, + { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208, upload-time = "2025-09-08T23:07:42.298Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766, upload-time = "2025-09-08T23:07:43.869Z" }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266, upload-time = "2025-09-08T23:09:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206, upload-time = "2025-09-08T23:09:41.902Z" }, + { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747, upload-time = "2025-09-08T23:09:43.741Z" }, + { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371, upload-time = "2025-09-08T23:09:45.575Z" }, + { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862, upload-time = "2025-09-08T23:09:47.448Z" }, ] [[package]] name = "rapidfuzz" version = "3.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900, upload-time = "2025-11-01T11:54:52.321Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376 }, - { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903 }, - { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655 }, - { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708 }, - { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106 }, - { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048 }, - { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020 }, - { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958 }, - { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043 }, - { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273 }, - { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875 }, + { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376, upload-time = "2025-11-01T11:52:29.175Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903, upload-time = "2025-11-01T11:52:31.239Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655, upload-time = "2025-11-01T11:52:32.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708, upload-time = "2025-11-01T11:52:34.618Z" }, + { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106, upload-time = "2025-11-01T11:52:36.069Z" }, + { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048, upload-time = "2025-11-01T11:52:37.936Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020, upload-time = "2025-11-01T11:52:39.657Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958, upload-time = "2025-11-01T11:52:41.017Z" }, + { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043, upload-time = "2025-11-01T11:52:42.465Z" }, + { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273, upload-time = "2025-11-01T11:52:44.005Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875, upload-time = "2025-11-01T11:52:45.405Z" }, ] [[package]] @@ -3710,33 +3688,33 @@ dependencies = [ { name = "rpds-py" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] [[package]] name = "regex" version = "2024.11.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, - { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, - { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, - { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, - { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, - { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, - { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, - { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, - { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, - { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, - { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, - { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, - { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, - { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, - { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, - { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674, upload-time = "2024-11-06T20:08:57.575Z" }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684, upload-time = "2024-11-06T20:08:59.787Z" }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589, upload-time = "2024-11-06T20:09:01.896Z" }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511, upload-time = "2024-11-06T20:09:04.062Z" }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149, upload-time = "2024-11-06T20:09:06.237Z" }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707, upload-time = "2024-11-06T20:09:07.715Z" }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702, upload-time = "2024-11-06T20:09:10.101Z" }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976, upload-time = "2024-11-06T20:09:11.566Z" }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397, upload-time = "2024-11-06T20:09:13.119Z" }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726, upload-time = "2024-11-06T20:09:14.85Z" }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098, upload-time = "2024-11-06T20:09:16.504Z" }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325, upload-time = "2024-11-06T20:09:18.698Z" }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277, upload-time = "2024-11-06T20:09:21.725Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197, upload-time = "2024-11-06T20:09:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714, upload-time = "2024-11-06T20:09:26.36Z" }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042, upload-time = "2024-11-06T20:09:28.762Z" }, ] [[package]] @@ -3749,9 +3727,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [package.optional-dependencies] @@ -3766,18 +3744,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, ] [[package]] name = "rfc3986-validator" version = "0.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, ] [[package]] @@ -3787,9 +3765,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lark" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, ] [[package]] @@ -3800,9 +3778,9 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393 }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, ] [[package]] @@ -3823,56 +3801,56 @@ wheels = [ name = "rignore" version = "0.7.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999 }, - { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824 }, - { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121 }, - { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813 }, - { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019 }, - { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822 }, - { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820 }, - { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050 }, - { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164 }, - { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028 }, - { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024 }, - { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531 }, - { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817 }, - { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001 }, - { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462 }, - { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918 }, - { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922 }, - { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987 }, - { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110 }, - { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339 }, - { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680 }, - { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045 }, - { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310 }, - { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998 }, - { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178 }, - { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190 }, +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999, upload-time = "2025-11-05T20:42:38.373Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824, upload-time = "2025-11-05T20:42:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121, upload-time = "2025-11-05T20:40:48.94Z" }, + { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813, upload-time = "2025-11-05T20:41:04.71Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019, upload-time = "2025-11-05T20:41:20.723Z" }, + { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822, upload-time = "2025-11-05T20:41:36.99Z" }, + { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820, upload-time = "2025-11-05T20:42:06.364Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050, upload-time = "2025-11-05T20:41:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164, upload-time = "2025-11-05T21:40:10.368Z" }, + { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028, upload-time = "2025-11-05T21:40:27.977Z" }, + { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024, upload-time = "2025-11-05T21:40:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531, upload-time = "2025-11-05T21:41:02.734Z" }, + { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817, upload-time = "2025-11-05T21:41:47.51Z" }, + { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001, upload-time = "2025-11-05T21:41:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462, upload-time = "2025-11-05T20:42:50.804Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918, upload-time = "2025-11-05T20:42:33.689Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922, upload-time = "2025-11-05T20:41:00.361Z" }, + { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987, upload-time = "2025-11-05T20:41:16.219Z" }, + { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110, upload-time = "2025-11-05T20:41:32.631Z" }, + { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339, upload-time = "2025-11-05T20:41:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680, upload-time = "2025-11-05T20:42:18.061Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045, upload-time = "2025-11-05T20:42:02.315Z" }, + { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310, upload-time = "2025-11-05T21:40:23.184Z" }, + { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998, upload-time = "2025-11-05T21:40:40.603Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178, upload-time = "2025-11-05T21:40:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190, upload-time = "2025-11-05T21:41:16.403Z" }, ] [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490 }, - { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751 }, - { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696 }, - { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136 }, - { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699 }, - { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022 }, - { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522 }, - { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579 }, - { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305 }, - { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503 }, - { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322 }, - { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792 }, - { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901 }, - { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823 }, + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, ] [[package]] @@ -3882,9 +3860,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 } +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 }, + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, ] [[package]] @@ -3894,35 +3872,35 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827 } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830 }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, ] [[package]] name = "safetensors" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, - { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430 }, - { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977 }, - { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890 }, - { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885 }, +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, ] [[package]] @@ -3935,13 +3913,13 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136 } +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221 }, - { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834 }, - { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938 }, - { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818 }, - { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969 }, + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, ] [[package]] @@ -3951,16 +3929,16 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870, upload-time = "2024-06-24T20:35:18.532Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226 }, - { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893 }, - { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258 }, - { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715 }, - { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038 }, - { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959 }, - { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514 }, - { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252 }, + { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226, upload-time = "2024-06-24T20:31:50.451Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893, upload-time = "2024-06-24T20:31:57.337Z" }, + { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258, upload-time = "2024-06-24T20:32:02.711Z" }, + { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715, upload-time = "2024-06-24T20:32:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038, upload-time = "2024-06-24T20:32:17.305Z" }, + { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959, upload-time = "2024-06-24T20:32:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514, upload-time = "2024-06-24T20:32:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252, upload-time = "2024-06-24T20:32:45.06Z" }, ] [[package]] @@ -3972,9 +3950,9 @@ dependencies = [ { name = "numpy" }, { name = "pandas" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, ] [[package]] @@ -3984,9 +3962,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "regex" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244, upload-time = "2021-12-15T21:56:14.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332, upload-time = "2021-12-15T21:56:12.508Z" }, ] [[package]] @@ -4020,16 +3998,16 @@ wheels = [ name = "sentencepiece" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106, upload-time = "2024-02-19T17:06:47.428Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979 }, - { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845 }, - { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472 }, - { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151 }, - { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931 }, - { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537 }, - { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747 }, - { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525 }, + { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979, upload-time = "2024-02-19T17:05:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845, upload-time = "2024-02-19T17:05:37.371Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472, upload-time = "2024-02-19T17:05:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151, upload-time = "2024-02-19T17:05:42.594Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931, upload-time = "2024-02-19T17:05:44.695Z" }, + { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537, upload-time = "2024-02-19T17:05:46.713Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747, upload-time = "2024-02-19T17:05:48.705Z" }, + { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525, upload-time = "2024-02-19T17:05:55.145Z" }, ] [[package]] @@ -4049,54 +4027,54 @@ wheels = [ name = "setuptools" version = "80.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958 } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486 }, + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "smmap" version = "5.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, ] [[package]] @@ -4132,7 +4110,7 @@ wheels = [ name = "sqlitedict" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846 } +sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846, upload-time = "2022-12-03T13:39:13.102Z" } [[package]] name = "sqlmodel" @@ -4142,7 +4120,7 @@ dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392, upload-time = "2024-08-31T09:43:24.088Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, ] @@ -4165,9 +4143,9 @@ dependencies = [ { name = "executing" }, { name = "pure-eval" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] [[package]] @@ -4178,9 +4156,9 @@ dependencies = [ { name = "anyio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033 }, + { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, ] [[package]] @@ -4231,9 +4209,9 @@ dependencies = [ { name = "torch" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481, upload-time = "2025-09-23T21:41:37.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395 }, + { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395, upload-time = "2025-09-23T21:41:35.369Z" }, ] [[package]] @@ -4243,27 +4221,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] [[package]] name = "tabulate" version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, ] [[package]] name = "tenacity" version = "9.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248 }, + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, ] [[package]] @@ -4295,21 +4273,21 @@ version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, ] [[package]] name = "threadpoolctl" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] [[package]] @@ -4320,15 +4298,15 @@ dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991 }, - { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798 }, - { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865 }, - { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856 }, - { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308 }, - { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697 }, - { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375 }, + { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991, upload-time = "2025-10-06T20:21:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798, upload-time = "2025-10-06T20:21:35.579Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865, upload-time = "2025-10-06T20:21:36.675Z" }, + { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856, upload-time = "2025-10-06T20:21:37.873Z" }, + { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308, upload-time = "2025-10-06T20:21:39.577Z" }, + { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697, upload-time = "2025-10-06T20:21:41.154Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375, upload-time = "2025-10-06T20:21:43.201Z" }, ] [[package]] @@ -4338,9 +4316,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, ] [[package]] @@ -4377,18 +4355,18 @@ wheels = [ name = "toml" version = "0.10.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, ] [[package]] name = "tomli" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392 } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408 }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] [[package]] @@ -4400,30 +4378,30 @@ dependencies = [ { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681 }, - { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036 }, - { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861 }, - { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222 }, + { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681, upload-time = "2025-11-12T15:19:56.48Z" }, + { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036, upload-time = "2025-11-12T15:21:01.886Z" }, + { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861, upload-time = "2025-11-12T15:21:30.145Z" }, + { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222, upload-time = "2025-11-12T15:20:46.223Z" }, ] [[package]] @@ -4435,9 +4413,9 @@ dependencies = [ { name = "packaging" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144, upload-time = "2023-03-10T22:02:20.455Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162 }, + { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162, upload-time = "2023-03-10T22:02:18.818Z" }, ] [[package]] @@ -4464,20 +4442,20 @@ name = "tqdm" version = "4.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] [[package]] name = "traitlets" version = "5.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, ] [[package]] @@ -4488,9 +4466,9 @@ dependencies = [ { name = "torch" }, { name = "transformers", extra = ["sentencepiece", "torch"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754 } +sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754, upload-time = "2025-06-15T13:34:38.522Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073, upload-time = "2025-06-15T13:34:37.468Z" }, ] [[package]] @@ -4509,9 +4487,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680 } +sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680, upload-time = "2025-11-25T15:51:30.139Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463 }, + { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463, upload-time = "2025-11-25T15:51:26.493Z" }, ] [package.optional-dependencies] @@ -4529,8 +4507,7 @@ name = "triton" version = "3.5.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/2e/f95e673222afa2c7f0c687d8913e98fcf2589ef0b1405de76894e37fe18f/triton-3.5.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f63e34dcb32d7bd3a1d0195f60f30d2aee8b08a69a0424189b71017e23dfc3d2", size = 159821655 }, - { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692 }, + { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692, upload-time = "2025-11-11T17:40:46.074Z" }, ] [[package]] @@ -4552,9 +4529,9 @@ wheels = [ name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] @@ -4564,9 +4541,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] @@ -4582,18 +4559,18 @@ wheels = [ name = "unidecode" version = "1.3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701 } +sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701, upload-time = "2024-01-11T11:58:35.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494 }, + { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494, upload-time = "2024-01-11T11:58:33.012Z" }, ] [[package]] name = "uri-template" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, ] [[package]] @@ -4621,7 +4598,7 @@ wheels = [ [package.optional-dependencies] standard = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "httptools" }, { name = "python-dotenv" }, { name = "pyyaml" }, @@ -4634,14 +4611,14 @@ standard = [ name = "uvloop" version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335 }, - { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903 }, - { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499 }, - { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133 }, - { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681 }, - { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261 }, + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" }, ] [[package]] @@ -4654,7 +4631,7 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799 } +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095 }, ] @@ -4672,18 +4649,18 @@ wheels = [ name = "watchdog" version = "6.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] [[package]] @@ -4693,67 +4670,67 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318 }, - { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478 }, - { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894 }, - { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065 }, - { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377 }, - { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837 }, - { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456 }, - { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614 }, - { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690 }, - { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459 }, - { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663 }, - { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453 }, - { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611 }, - { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889 }, - { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616 }, - { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413 }, + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" }, ] [[package]] name = "wcwidth" version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293 } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286 }, + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, ] [[package]] name = "webcolors" version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, ] [[package]] name = "webencodings" version = "0.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, ] [[package]] name = "websocket-client" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, ] [[package]] name = "websockets" version = "15.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423 }, { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080 }, @@ -4791,9 +4768,9 @@ wheels = [ name = "widgetsnbextension" version = "4.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, ] [[package]] @@ -4803,59 +4780,59 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581480705f912040172f6570cc12e68a245ba9258c32ef/wikipedia_api-0.8.1.tar.gz", hash = "sha256:b31e93b3f5407c1a1ba413ed7326a05379a3c270df6cf6a211aca67a14c5658b", size = 19934 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581480705f912040172f6570cc12e68a245ba9258c32ef/wikipedia_api-0.8.1.tar.gz", hash = "sha256:b31e93b3f5407c1a1ba413ed7326a05379a3c270df6cf6a211aca67a14c5658b", size = 19934, upload-time = "2025-01-19T23:44:33.488Z" } [[package]] name = "wrapt" version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040 } +sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481 }, - { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692 }, - { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574 }, - { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688 }, - { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698 }, - { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096 }, - { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878 }, - { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298 }, - { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361 }, - { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035 }, - { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383 }, - { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894 }, - { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046 }, + { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481, upload-time = "2025-11-07T00:43:11.103Z" }, + { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692, upload-time = "2025-11-07T00:43:13.697Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574, upload-time = "2025-11-07T00:43:14.967Z" }, + { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688, upload-time = "2025-11-07T00:43:18.275Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698, upload-time = "2025-11-07T00:43:19.407Z" }, + { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096, upload-time = "2025-11-07T00:43:16.5Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878, upload-time = "2025-11-07T00:43:20.81Z" }, + { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298, upload-time = "2025-11-07T00:43:22.229Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361, upload-time = "2025-11-07T00:43:24.301Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035, upload-time = "2025-11-07T00:43:28.96Z" }, + { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383, upload-time = "2025-11-07T00:43:25.804Z" }, + { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894, upload-time = "2025-11-07T00:43:27.074Z" }, + { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, ] [[package]] name = "xmltodict" version = "0.14.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942 } +sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942, upload-time = "2024-10-16T06:10:29.683Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981 }, + { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981, upload-time = "2024-10-16T06:10:27.649Z" }, ] [[package]] name = "xxhash" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160 } +sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845 }, - { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807 }, - { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786 }, - { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830 }, - { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606 }, - { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872 }, - { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217 }, - { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139 }, - { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669 }, - { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018 }, - { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058 }, - { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628 }, - { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577 }, - { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487 }, - { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863 }, + { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" }, + { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" }, + { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" }, + { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" }, + { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" }, + { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" }, + { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" }, + { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" }, ] [[package]] @@ -4867,32 +4844,32 @@ dependencies = [ { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169 } +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517 }, - { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495 }, - { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400 }, - { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545 }, - { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598 }, - { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893 }, - { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240 }, - { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965 }, - { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026 }, - { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637 }, - { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082 }, - { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811 }, - { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223 }, - { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118 }, - { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852 }, - { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012 }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814 }, + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] [[package]] name = "zipp" version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ] From 4f844a2c348a7541b4cc9c82c8b20c5b752b78b0 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Tue, 10 Feb 2026 10:54:02 -0300 Subject: [PATCH 032/101] Endpoint /disambiguate with LLM Inference (#72) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Changes in old 07 notebook adding the usage of the disambiguate endpoint and its own name * New token counter to check if the LLM inference won't allucinate * New tokenizer function for token counting and proessing specifics documents * Batch optimization feature in llm-inference function * Mapping feature added to llm-inference function * Updated the /disambiguate endpoint to return DocumentAnnotations similar to the NER predictions, now enriched with role and entity_id fields where applicable. * New /disambiguatev2 endpoint which makes the LLM inference and return the DocumentAnnotations list with the role and the canonincal_entity_id where applicable. When there is a prediction that wasn't mapped the program generates a canonical_entity_id * New /disambiguatev2 endpoint which makes the LLM inference and return the DocumentAnnotations list with the role and the canonincal_entity_id where applicable. When there is a prediction that wasn't mapped the program generates a canonical_entity_id * New /disambiguatev2 endpoint which makes the LLM inference and return the DocumentAnnotations list with the role and the canonincal_entity_id where applicable. When there is a prediction that wasn't mapped the program generates a canonical_entity_id * New updates on endpoint /disambiguatev2 and notebook 07 * Cleaned code in anonymizer.py and utils.py following Raúl comments * New classes defined for LLM prompts to validate each set of prompts per label before the LLM inference * Sorted canonical entities before LLM inference to avoid (or trying to) processing two or more canonical entities that are only one in separate batches * Cleaned anonymizer.py script and experimental notebook 07 discarding the old pre-cluster endpoint. * Cleaned anonymizer.py script and experimental notebook 07 discarding the old pre-cluster endpoint. * Cleaned anonymizer.py script and experimental notebook 07 discarding the old pre-cluster endpoint. New disambiguation.py script to store functions to pre-clusterize the canonical entities. * Cleaned anonymizer.py script and experimental notebook 07 discarding the old pre-cluster endpoint. New disambiguation.py script to store functions to pre-clusterize the canonical entities. * Code cleaned following Juli's comments regarding the new /disambiguate endpoint * Remove unused relations field from CanonicalEntity class for LLM inference phase * Final changes to the code adding the entity_disambiguation.yaml to handle the prompts * Add entity disambiguation utilities and enhance canonical entity processing - Introduced new utility functions for entity disambiguation in `fuzzy.py`. - Implemented `assign_label_instances` and `map_canonical_entities_ner_preds` in `core.py`. - Added LLM inference capabilities in `llm.py` for refining canonical entities. - Updated `entities.py` to include `aymurai_label_instance` for ordered label indexing. * Refactor anonymizer and paragraph modules for improved entity disambiguation and serialization * Remove unused logger import from paragraph module * Reviewed code and added some features to 07 experiment notebook * Implement label policies for disambiguation and anonymization; enhance entity processing and prediction mapping * New datetime formatter function and changes in old code, there is a bug with my OS that unsupports the setlocale * New functioanlity added to get_canonical_dates for dates with the same day and month * New functioanlity added to get_canonical_dates for dates with the same day and month * 🐛 Fix entity handling in anonymizer and datapublic routers when use_cache is disabled to improve label processing * Remove commented-out code * DatetimeFormatter used after NER predictions in postprocess so we only have to take the datetime from aymurai_label_subclass to build the canonical entities from dates * Fix locale setting for date formatting to ensure correct month name handling * Add docstring for get_canonical_dates function to clarify input and output * Remove DIRECCION prompt templates * Update notebook formatting, remove unused MODE param and improve code readability * Update uv.lock --- .env.common | 2 +- .../routers/anonymizer/anonymizer.py | 384 +++- .../api/endpoints/routers/anonymizer/utils.py | 212 -- .../routers/datapublic/datapublic.py | 9 +- .../database/crud/anonymization/paragraph.py | 110 +- aymurai/meta/api_interfaces.py | 28 + aymurai/meta/entities.py | 18 +- aymurai/settings.py | 27 + .../transforms/datetime_formatter/__init__.py | 1 + aymurai/transforms/datetime_formatter/core.py | 4 +- .../transforms/datetime_formatter/patterns.py | 7 + .../utils/entity_disambiguation/__init__.py | 21 + aymurai/utils/entity_disambiguation/core.py | 128 ++ .../entity_disambiguation/date_formatter.py | 45 + aymurai/utils/entity_disambiguation/fuzzy.py | 199 ++ aymurai/utils/entity_disambiguation/llm.py | 379 ++++ .../07-LLM-entities-disambiguation.ipynb | 1713 +++++++++++++++ .../07-PER-entities-disambiguation.ipynb | 1487 ------------- .../08-disambiguation-endpoint-smoke.ipynb | 300 ++- .../09-date-formatter-disambiguation.ipynb | 360 +++ resources/llm/entity_disambiguation.yml | 69 + .../production/flair-anonymizer/pipeline.json | 4 + uv.lock | 1949 +++++++++-------- 23 files changed, 4690 insertions(+), 2766 deletions(-) delete mode 100644 aymurai/api/endpoints/routers/anonymizer/utils.py create mode 100644 aymurai/utils/entity_disambiguation/__init__.py create mode 100644 aymurai/utils/entity_disambiguation/core.py create mode 100644 aymurai/utils/entity_disambiguation/date_formatter.py create mode 100644 aymurai/utils/entity_disambiguation/fuzzy.py create mode 100644 aymurai/utils/entity_disambiguation/llm.py create mode 100644 notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb delete mode 100644 notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb create mode 100644 notebooks/experiments/entity-disambiguation/09-date-formatter-disambiguation.ipynb create mode 100644 resources/llm/entity_disambiguation.yml diff --git a/.env.common b/.env.common index 38601115..25d41bb8 100644 --- a/.env.common +++ b/.env.common @@ -16,4 +16,4 @@ MLFLOW_TRACKING_URI=http://localhost:5000 MLFLOW_S3_ENDPOINT_URL=http://localhost:9002 MLFLOW_S3_IGNORE_TLS=true MLFLOW_EXPERIMENT_NAME=entity-disambiguation -MLFLOW_ARTIFACT_ROOT=s3://mlflow/artifacts +MLFLOW_ARTIFACT_ROOT=s3://mlflow/artifacts \ No newline at end of file diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 63025082..dc7f3d56 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -5,18 +5,12 @@ from threading import Lock import torch -from fastapi import Body, Depends, Form, HTTPException, Query, UploadFile +from fastapi import Body, Depends, Form, Query, UploadFile from fastapi.responses import FileResponse from fastapi.routing import APIRouter from sqlmodel import Session from starlette.background import BackgroundTask -from aymurai.api.endpoints.routers.anonymizer.utils import ( - PROCESSOR_MAP, - SCORER_MAP, - build_canonical_entities, - resolve_processor, -) from aymurai.api.utils import load_pipeline from aymurai.database.crud.anonymization.document import anonymization_document_create from aymurai.database.crud.anonymization.paragraph import ( @@ -24,7 +18,7 @@ anonymization_paragraph_create, anonymization_paragraph_read, ) -from aymurai.database.schema import AnonymizationParagraph +from aymurai.database.schema import AnonymizationParagraph, AnonymizationParagraphCreate from aymurai.database.session import get_session from aymurai.database.utils import data_to_uuid, text_to_uuid from aymurai.logger import get_logger @@ -32,12 +26,20 @@ DocLabel, DocumentAnnotations, DocumentInformation, + LabelPolicy, + PromptLibrary, TextRequest, ) -from aymurai.meta.entities import CanonicalEntities from aymurai.settings import settings from aymurai.text.anonymization import DocAnonymizer from aymurai.text.extraction import MIMETYPE_EXTENSION_MAPPER +from aymurai.utils.entity_disambiguation import ( + build_canonical_entities, + get_canonical_dates, + llm_canonical_entities_inference, + load_prompts_from_yaml, + map_canonical_entities_ner_preds, +) from aymurai.utils.misc import get_element logger = get_logger(__name__) @@ -51,6 +53,100 @@ router = APIRouter() +def _entities_to_doclabels(entities: list[dict]) -> list[DocLabel]: + """ + Convert raw entities to DocLabel objects. + + Args: + entities (list[dict]): List of entity dictionaries. + + Returns: + list[DocLabel]: List of DocLabel objects. + """ + doclabels: list[DocLabel] = [] + + for ent in entities: + try: + doclabels.append( + DocLabel.model_validate( + { + "text": ent.get("text", ""), + "start_char": ent.get("start_char"), + "end_char": ent.get("end_char"), + "attrs": ent.get("attrs", {}), + } + ) + ) + except Exception as exc: # keep going if a single entity is malformed + logger.warning(f"Skipping invalid entity for DocLabel: {exc}") + + return doclabels + + +def _merge_label_policies( + request_policies: dict[str, LabelPolicy] | None, +) -> dict[str, LabelPolicy]: + """ + Merges label policies from settings and request, with request policies taking precedence. + + Args: + request_policies (dict[str, LabelPolicy] | None): Per-label policies provided in the request body. + + Returns: + dict[str, LabelPolicy]: Effective per-label policies after merging settings and request policies. + """ + policies: dict[str, LabelPolicy] = {} + + if settings.DISAMBIGUATION_LABEL_POLICIES: + for label, policy in settings.DISAMBIGUATION_LABEL_POLICIES.items(): + incoming = LabelPolicy.model_validate(policy) + current = policies.get(label, LabelPolicy()) + if incoming.disambiguation is not None: + current.disambiguation = incoming.disambiguation + if incoming.anonymize is not None: + current.anonymize = incoming.anonymize + policies[label] = current + + if request_policies: + for label, policy in request_policies.items(): + incoming = LabelPolicy.model_validate(policy) + current = policies.get(label, LabelPolicy()) + if incoming.disambiguation is not None: + current.disambiguation = incoming.disambiguation + if incoming.anonymize is not None: + current.anonymize = incoming.anonymize + policies[label] = current + + return policies + + +def _should_anonymize_label( + label: DocLabel, + label_policies: dict[str, LabelPolicy], +) -> bool: + """ + Determines whether a given label should be anonymized based on its attributes and the effective label policies. + + Args: + label (DocLabel): The document label to evaluate for anonymization. + label_policies (dict[str, LabelPolicy]): Effective per-label policies that may override default anonymization behavior. + + Returns: + bool: True if the label should be anonymized, False otherwise. + """ + if label.attrs and label.attrs.aymurai_anonymize is not None: + return bool(label.attrs.aymurai_anonymize) + + policy = label_policies.get(label.attrs.aymurai_label) if label.attrs else None + if policy is None: + return True + + if policy.anonymize is None: + return True + + return bool(policy.anonymize) + + # MARK: Predict @router.post("/predict", response_model=DocumentInformation) async def anonymizer_paragraph_predict( @@ -85,8 +181,8 @@ async def anonymizer_paragraph_predict( logger.info(f"cache loaded from key: {paragraph_id}") logger.debug(f"{cached_prediction}") - labels = cached_prediction.prediction - return DocumentInformation(document=cached_prediction.text, labels=labels or []) + labels = _entities_to_doclabels(cached_prediction.prediction or []) + return DocumentInformation(document=cached_prediction.text, labels=labels) logger.info("Running prediction") item = [{"path": "empty", "data": {"doc.text": text_request.text}}] @@ -100,21 +196,22 @@ async def anonymizer_paragraph_predict( processed = pipeline.postprocess([processed]) text = get_element(processed[0], ["data", "doc.text"]) or "" - labels = get_element(processed[0], ["predictions", "entities"]) or [] + raw_entities = get_element(processed[0], ["predictions", "entities"]) or [] + labels = _entities_to_doclabels(raw_entities) if use_cache: logger.info(f"saving in cache: {paragraph_id}") - paragraph = AnonymizationParagraph( - id=paragraph_id, + paragraph = AnonymizationParagraphCreate( text=text, prediction=labels, ) paragraph = anonymization_paragraph_create(paragraph, session=session) - return DocumentInformation(document=text, labels=paragraph.prediction) + return DocumentInformation(document=text, labels=labels) -@router.post("/disambiguate", response_model=CanonicalEntities) +# MARK: Disambiguate +@router.post("/disambiguate", response_model=DocumentAnnotations) async def anonymizer_disambiguate( paragraphs: list[DocumentInformation] = Body( ..., @@ -122,51 +219,215 @@ async def anonymizer_disambiguate( "List of per-paragraph predictions returned by /anonymizer/predict." ), ), + custom_prompts: PromptLibrary = Body( + default_factory=PromptLibrary, + description=( + "Set of prompts, user and system, for each label if it is provided." + ), + ), + label_policies: dict[str, LabelPolicy] + | None = Body( + None, + description=( + "Optional per-label policy overrides for disambiguation/anonymization." + ), + ), target_labels: list[str] | None = Query( None, - description="Optional label filter, e.g. PER,DNI.", - ), - threshold: int = Query( - 70, - description="Minimum similarity score (0-100) to cluster entities.", - ), - scorer: str = Query( - "token_set_ratio", - description="RapidFuzz scorer name for similarity.", - ), - processor: str = Query( - "light_normalizer", - description="Text processor to normalize before similarity.", + description=( + "Optional label filter for LLM refinement (e.g., PER,DNI). " + "Fuzzy clustering still runs across all detected labels." + ), ), -) -> CanonicalEntities: + session: Session = Depends(get_session), +) -> DocumentAnnotations: """ - Prototype endpoint for canonical entity grouping using fuzzy matching. + Performs canonical entity disambiguation using fuzzy matching and LLM refinement. + + Args: + paragraphs: A list of DocumentInformation objects containing the NER + predictions per paragraph that need to be disambiguated. + custom_prompts: A PromptLibrary object containing optional system and + user prompts. If not provided, the service uses the default prompts + configured in the environment. + label_policies: Optional per-label disambiguation/anonymization policies. + target_labels: An optional list of entity labels to refine via LLM + (e.g., ["PER", "DNI"]). Fuzzy clustering still runs across all + detected labels. + Returns: + DocumentAnnotations: The original annotations enriched with + 'canonical_entity_id' and 'role' fields for each resolved mention. """ - if threshold < 0 or threshold > 100: - raise HTTPException(status_code=400, detail="threshold must be 0-100.") + logger.info( + "disambiguation start: paragraphs=%d", + len(paragraphs), + ) + + labels = [label for paragraph in paragraphs for label in (paragraph.labels or [])] + effective_policies = _merge_label_policies(label_policies) + logger.info("disambiguation labels: %d", len(labels)) - scorer_fn = SCORER_MAP.get(scorer.lower()) - if scorer_fn is None: - raise HTTPException(status_code=400, detail=f"Unsupported scorer: {scorer}") + prompt_library = load_prompts_from_yaml() + + all_detected_labels = { + label.attrs.aymurai_label + for label in labels + if label.attrs and label.attrs.aymurai_label + } + + default_llm_labels = ( + target_labels if target_labels else list(prompt_library.as_dict.keys()) + ) + + llm_labels: list[str] = [] + fuzzy_labels: set[str] = set() + + for label in all_detected_labels: + policy = effective_policies.get(label) + if policy and policy.disambiguation == "llm": + llm_labels.append(label) + fuzzy_labels.add(label) + continue + if policy and policy.disambiguation == "fuzzy": + fuzzy_labels.add(label) + continue + if policy and policy.disambiguation == "none": + continue + + if label in default_llm_labels: + llm_labels.append(label) + fuzzy_labels.add(label) + else: + fuzzy_labels.add(label) + + effective_disambiguation_by_label: dict[str, str] = {} + for label in all_detected_labels: + if label in llm_labels: + effective_disambiguation_by_label[label] = "llm" + elif label in fuzzy_labels: + effective_disambiguation_by_label[label] = "fuzzy" + else: + effective_disambiguation_by_label[label] = "none" + logger.info( + "disambiguation targets: detected=%s llm=%s", + sorted(all_detected_labels), + llm_labels, + ) - if processor.lower() not in PROCESSOR_MAP: - raise HTTPException( - status_code=400, detail=f"Unsupported processor: {processor}" + canonical_entities = ( + build_canonical_entities( + labels, + target_labels=[label for label in fuzzy_labels if label != "FECHA"] + if fuzzy_labels + else None, + threshold=settings.THRESHOLD, ) - processor_fn = resolve_processor(processor) + if fuzzy_labels + else [] + ) - labels = [label for paragraph in paragraphs for label in (paragraph.labels or [])] + canonical_entities += get_canonical_dates(labels=labels) + + logger.info( + "fuzzy clustering produced %d canonical entities", len(canonical_entities) + ) + + # --- LLM REFINEMENT (policy-driven) --- + canonical_entities_llm = [] + + for label in llm_labels: + logger.info("llm refinement: label=%s", label) + prompt_set = custom_prompts.get(label) or prompt_library.get(label) + + if ( + not prompt_set + or not prompt_set.system.strip() + or not prompt_set.user.strip() + ): + logger.info("llm refinement skipped: missing prompt for label=%s", label) + continue + + entities_for_this_label = [ + e for e in canonical_entities if e.aymurai_label == label + ] + + if not entities_for_this_label: + logger.info("llm refinement skipped: no entities for label=%s", label) + continue + logger.info( + "llm refinement input: label=%s entities=%d", + label, + len(entities_for_this_label), + ) - target_set = {label.strip() for label in target_labels} if target_labels else None - canonical_entities = build_canonical_entities( - labels, - target_labels=target_set, - threshold=threshold, - scorer=scorer_fn, - processor=processor_fn, + llm_response = llm_canonical_entities_inference( + paragraphs=paragraphs, + canonical_entities_pre_cluster=entities_for_this_label, + system_prompt=prompt_set.system, + user_prompt_template=prompt_set.user, + model=settings.MODEL, + context_window_length=settings.CONTEXT_WINDOW_LENGTH, + model_context=settings.MODEL_CONTEXT, + token_limit_frac=settings.TOKEN_LIMIT_FRAC, + tokenizer_model=settings.TOKENIZER_MODEL, + target_label=label, + temperature=settings.TEMPERATURE, + decompose_by=settings.DECOMPOSE_BY, + ) + + if llm_response: + canonical_entities_llm.extend(llm_response) + logger.info( + "llm refinement output: label=%s entities=%d", + label, + len(llm_response), + ) + + canonical_entities_merged = canonical_entities_llm + [ + ce for ce in canonical_entities if ce.aymurai_label not in set(llm_labels) + ] + logger.info( + "disambiguation merge: llm=%d fuzzy_only=%d total=%d", + len(canonical_entities_llm), + len(canonical_entities_merged) - len(canonical_entities_llm), + len(canonical_entities_merged), + ) + + predictions = map_canonical_entities_ner_preds( + predictions=paragraphs, + canonical_entities=canonical_entities_merged, + force_labels=set(llm_labels), + ) + + for document in predictions: + for label in document.labels or []: + label.attrs.aymurai_disambiguation = effective_disambiguation_by_label.get( + label.attrs.aymurai_label, "fuzzy" + ) + + policy = effective_policies.get(label.attrs.aymurai_label) + label.attrs.aymurai_anonymize = ( + policy.anonymize if policy and policy.anonymize is not None else True + ) + + paragraph_updates = [ + AnonymizationParagraphCreate( + text=paragraph.document, + prediction=paragraph.labels or [], + ) + for paragraph in predictions + ] + anonymization_paragraph_batch_create_update(paragraph_updates, session=session) + logger.info( + "disambiguation persisted predictions for %d paragraphs", + len(paragraph_updates), + ) + + return DocumentAnnotations( + data=predictions, + label_policies=effective_policies if effective_policies else None, ) - return CanonicalEntities(canonical_entities=canonical_entities) # MARK: Validate @@ -241,12 +502,12 @@ async def anonymizer_compile_document( annots_json = json.loads(annotations) annots = DocumentAnnotations.model_validate(annots_json) logger.info(f"processing annotations => {annots}") + effective_policies = _merge_label_policies(annots.label_policies) # Add paragraphs to the database # validation MUST be at least an empty list, to remember user feedback paragraphs = [ - AnonymizationParagraph( - id=text_to_uuid(paragraph.document), + AnonymizationParagraphCreate( text=paragraph.document, validation=paragraph.labels or [], ) @@ -267,11 +528,28 @@ async def anonymizer_compile_document( # Anonymize the document doc_anonymizer = DocAnonymizer() + filtered_annotations = [] + for paragraph in annots.data: + filtered_labels = [ + label + for label in (paragraph.labels or []) + if _should_anonymize_label(label, effective_policies) + ] + filtered_annotations.append( + DocumentInformation( + document=paragraph.document, + labels=filtered_labels, + ) + ) + if suffix == ".docx": item = {"path": tmp_filename} doc_anonymizer( item, - [document_information.model_dump() for document_information in annots.data], + [ + document_information.model_dump() + for document_information in filtered_annotations + ], tmp_dir, ) logger.info(f"saved temp file on local storage => {tmp_filename}") @@ -282,7 +560,7 @@ async def anonymizer_compile_document( doc_anonymizer.replace_labels_in_text(document_information.model_dump()) .replace("<", "<") .replace(">", ">") - for document_information in annots.data + for document_information in filtered_annotations ] with open(tmp_filename, "w") as f: f.write("\n".join(anonymized_doc)) diff --git a/aymurai/api/endpoints/routers/anonymizer/utils.py b/aymurai/api/endpoints/routers/anonymizer/utils.py deleted file mode 100644 index 9c25cea2..00000000 --- a/aymurai/api/endpoints/routers/anonymizer/utils.py +++ /dev/null @@ -1,212 +0,0 @@ -import re -import unicodedata -from collections import Counter -from typing import Callable, Iterable - -from rapidfuzz import process -from rapidfuzz.fuzz import ( - WRatio, - partial_ratio, - partial_token_set_ratio, - partial_token_sort_ratio, - ratio, - token_set_ratio, - token_sort_ratio, -) - -from aymurai.meta.api_interfaces import DocLabel -from aymurai.meta.entities import CanonicalEntity - -__all__ = [ - "SCORER_MAP", - "PROCESSOR_MAP", - "build_canonical_entities", - "resolve_processor", -] - -SCORER_MAP = { - "ratio": ratio, - "partial_ratio": partial_ratio, - "token_sort_ratio": token_sort_ratio, - "token_set_ratio": token_set_ratio, - "partial_token_set_ratio": partial_token_set_ratio, - "partial_token_sort_ratio": partial_token_sort_ratio, - "wratio": WRatio, -} - -PROCESSOR_MAP = { - "none": None, - "light_normalizer": "light_normalizer", - "hard_normalizer": "hard_normalizer", - "legal_text_normalizer": "legal_text_normalizer", -} - - -def hard_normalizer(text: str) -> str: - if not text: - return "" - - normalized = "".join( - char - for char in unicodedata.normalize("NFD", text) - if unicodedata.category(char) != "Mn" - ) - normalized = re.sub(r"[.,\-]", " ", normalized) - normalized = " ".join(normalized.lower().split()) - return normalized - - -def light_normalizer(text: str) -> str: - return text.lower().strip() if text else "" - - -def legal_text_normalizer(text: str) -> str: - if not text: - return "" - - normalized = "".join( - char - for char in unicodedata.normalize("NFD", text) - if unicodedata.category(char) != "Mn" - ) - normalized = normalized.lower() - - legal_titles = r"\b(dr|dra|sr|sra|expte|nro|no|pcia)\b\.?" - normalized = re.sub(legal_titles, "", normalized) - - stopwords = r"\b(de|del|la|las|el|los|y|en)\b" - normalized = re.sub(stopwords, "", normalized) - normalized = re.sub(r"[^\w\s]", "", normalized) - - return " ".join(normalized.split()) - - -def resolve_processor(name: str) -> Callable[[str], str] | None: - key = name.lower() - mapped = PROCESSOR_MAP.get(key) - if mapped is None: - return None - if mapped == "light_normalizer": - return light_normalizer - if mapped == "hard_normalizer": - return hard_normalizer - if mapped == "legal_text_normalizer": - return legal_text_normalizer - return None - - -def cluster_with_cdist( - *, - items: list[dict[str, str]], - threshold: int, - scorer: Callable[[str, str], float], - processor: Callable[[str], str] | None, -) -> list[list[tuple[str, str, str]]]: - if not items: - return [] - - entities = [item.get("text", "") for item in items] - labels = [item.get("aymurai_label", "UNKNOWN") for item in items] - - if processor: - normed = [processor(entity) for entity in entities] - else: - normed = [str(entity) for entity in entities] - - sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold) - - parent = list(range(len(normed))) - - def find(idx: int) -> int: - if parent[idx] == idx: - return idx - parent[idx] = find(parent[idx]) - return parent[idx] - - def union(left: int, right: int) -> None: - root_left, root_right = find(left), find(right) - if root_left != root_right: - parent[root_right] = root_left - - n = len(normed) - for i in range(n): - for j in range(i + 1, n): - if sim[i][j] >= threshold: - union(i, j) - - clusters_map: dict[int, list[tuple[str, str, str]]] = {} - for idx in range(n): - root = find(idx) - clusters_map.setdefault(root, []).append( - (entities[idx], normed[idx], labels[idx]) - ) - - return list(clusters_map.values()) - - -def parse_item(item: tuple[str, str, str]) -> tuple[str, str, str]: - orig, norm, label = item - return label, orig, norm - - -def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str: - labels = [label for label, _, _ in parsed_items] - return Counter(labels).most_common(1)[0][0] - - -def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str: - return max(parsed_items, key=lambda item: len(item[1]))[1] - - -def clusters_to_canonical_entities( - clusters: list[list[tuple[str, str, str]]], -) -> list[CanonicalEntity]: - canonical_entities = [] - for cluster in clusters: - parsed = [parse_item(item) for item in cluster] - label = pick_cluster_label(parsed) - canonical_text = pick_canonical_text(parsed) - aliases = sorted({orig for _, orig, _ in parsed}) - canonical_entities.append( - CanonicalEntity( - aymurai_label=label, - canonical_text=canonical_text, - aliases=aliases, - attributes={}, - relations=[], - ) - ) - return canonical_entities - - -def build_canonical_entities( - labels: Iterable[DocLabel], - *, - target_labels: set[str] | None = None, - threshold: int, - scorer: Callable[[str, str], float], - processor: Callable[[str], str] | None, -) -> list[CanonicalEntity]: - grouped: dict[str, list[dict[str, str]]] = {} - for label in labels: - attrs = label.attrs - if not attrs or not attrs.aymurai_label: - continue - if target_labels and attrs.aymurai_label not in target_labels: - continue - alias = attrs.aymurai_alt_text or label.text - grouped.setdefault(attrs.aymurai_label, []).append( - {"text": alias, "aymurai_label": attrs.aymurai_label} - ) - - canonical_entities = [] - for items in grouped.values(): - clusters = cluster_with_cdist( - items=items, - threshold=threshold, - scorer=scorer, - processor=processor, - ) - canonical_entities.extend(clusters_to_canonical_entities(clusters)) - - return canonical_entities diff --git a/aymurai/api/endpoints/routers/datapublic/datapublic.py b/aymurai/api/endpoints/routers/datapublic/datapublic.py index b83ae0d0..62af6761 100644 --- a/aymurai/api/endpoints/routers/datapublic/datapublic.py +++ b/aymurai/api/endpoints/routers/datapublic/datapublic.py @@ -2,16 +2,16 @@ from threading import Lock import torch -from fastapi import Body, Depends, Query, HTTPException +from fastapi import Body, Depends, HTTPException, Query from fastapi.routing import APIRouter from pydantic import UUID5 from sqlmodel import Session from aymurai.api.utils import load_pipeline from aymurai.database.schema import ( - DataPublicParagraph, DataPublicDocument, DataPublicDocumentParagraph, + DataPublicParagraph, ) from aymurai.database.session import get_session from aymurai.database.utils import text_to_uuid @@ -72,6 +72,7 @@ async def predict_over_text( text = get_element(processed[0], ["data", "doc.text"]) or "" labels = get_element(processed[0], ["predictions", "entities"]) or [] + paragraph: DataPublicParagraph | None = None if use_cache: logger.info(f"saving in cache: {paragraph_id}") @@ -92,7 +93,9 @@ async def predict_over_text( # paragraph = datapublic_paragraph_create(paragraph, session=session) - return DocumentInformation(document=text, labels=paragraph.prediction) + return DocumentInformation( + document=text, labels=paragraph.prediction if paragraph else labels + ) # MARK: Validate Paragraph diff --git a/aymurai/database/crud/anonymization/paragraph.py b/aymurai/database/crud/anonymization/paragraph.py index 593ebbdc..1d169036 100644 --- a/aymurai/database/crud/anonymization/paragraph.py +++ b/aymurai/database/crud/anonymization/paragraph.py @@ -1,5 +1,6 @@ import uuid +from pydantic import TypeAdapter from sqlmodel import Session from aymurai.database.schema import ( @@ -8,9 +9,42 @@ AnonymizationParagraphUpdate, ) from aymurai.database.utils import text_to_uuid -from aymurai.logger import get_logger +from aymurai.meta.api_interfaces import DocLabel -logger = get_logger(__name__) + +_DOC_LABELS_ADAPTER = TypeAdapter(list[DocLabel]) + + +def _serialize_doclabels(value: list[DocLabel] | None): + """ + Serializes DocLabel objects into JSON-compatible data structures. + + Args: + value (list[DocLabel] | None): DocLabel list to serialize. + + Returns: + list[dict] | None: JSON-safe list of labels, or None if input is None. + """ + if value is None: + return None + return _DOC_LABELS_ADAPTER.dump_python(value, mode="json") + + +def _normalize_paragraph_payload(payload: dict) -> dict: + """ + Normalizes paragraph payload fields for JSON storage. + + Args: + payload (dict): Paragraph payload possibly containing DocLabel objects. + + Returns: + dict: Payload with JSON-serializable prediction/validation fields. + """ + if "prediction" in payload: + payload["prediction"] = _serialize_doclabels(payload.get("prediction")) + if "validation" in payload: + payload["validation"] = _serialize_doclabels(payload.get("validation")) + return payload def anonymization_paragraph_create( @@ -18,7 +52,19 @@ def anonymization_paragraph_create( session: Session, override: bool = False, ) -> AnonymizationParagraph: - new_paragraph = AnonymizationParagraph(**paragraph_in.model_dump()) + """ + Creates a new anonymization paragraph record. + + Args: + paragraph_in (AnonymizationParagraphCreate): Paragraph creation payload. + session (Session): Database session. + override (bool): If True, delete any existing paragraph with the same ID. + + Returns: + AnonymizationParagraph: The persisted paragraph record. + """ + payload = _normalize_paragraph_payload(paragraph_in.model_dump()) + new_paragraph = AnonymizationParagraph(**payload) if override: existing = session.get(AnonymizationParagraph, new_paragraph.id) @@ -36,6 +82,16 @@ def anonymization_paragraph_read( paragraph_id: uuid.UUID, session: Session, ) -> AnonymizationParagraph | None: + """ + Reads a paragraph record by ID. + + Args: + paragraph_id (uuid.UUID): Paragraph UUID. + session (Session): Database session. + + Returns: + AnonymizationParagraph | None: Paragraph record if found. + """ return session.get(AnonymizationParagraph, paragraph_id) @@ -44,12 +100,26 @@ def anonymization_paragraph_update( paragraph_in: AnonymizationParagraphUpdate, session: Session, ) -> AnonymizationParagraph: + """ + Updates an existing paragraph record. + + Args: + paragraph_id (uuid.UUID): Paragraph UUID to update. + paragraph_in (AnonymizationParagraphUpdate): Update payload. + session (Session): Database session. + + Returns: + AnonymizationParagraph: The updated paragraph record. + """ paragraph = session.get(AnonymizationParagraph, paragraph_id) if not paragraph: raise ValueError(f"Paragraph not found: {paragraph_id}") - for field, value in paragraph_in.model_dump(exclude_none=True).items(): + payload = _normalize_paragraph_payload( + paragraph_in.model_dump(exclude_none=True, mode="json") + ) + for field, value in payload.items(): setattr(paragraph, field, value) session.add(paragraph) @@ -59,6 +129,16 @@ def anonymization_paragraph_update( def anonymization_paragraph_delete(paragraph_id: uuid.UUID, session: Session): + """ + Deletes a paragraph record by ID. + + Args: + paragraph_id (uuid.UUID): Paragraph UUID to delete. + session (Session): Database session. + + Returns: + None + """ paragraph = session.get(AnonymizationParagraph, paragraph_id) if not paragraph: @@ -74,6 +154,16 @@ def anonymization_paragraph_delete(paragraph_id: uuid.UUID, session: Session): def anonymization_paragraph_batch_create_update( paragraphs_in: list[AnonymizationParagraphCreate], session: Session ) -> list[AnonymizationParagraph]: + """ + Creates or updates a batch of paragraph records. + + Args: + paragraphs_in (list[AnonymizationParagraphCreate]): Paragraph payloads. + session (Session): Database session. + + Returns: + list[AnonymizationParagraph]: Persisted paragraph records. + """ paragraphs = [] for p_in in paragraphs_in: @@ -81,13 +171,15 @@ def anonymization_paragraph_batch_create_update( paragraph = session.get(AnonymizationParagraph, paragraph_id) if paragraph: - update = AnonymizationParagraphUpdate(**p_in.model_dump()) - - for field, value in update.model_dump(exclude_none=True).items(): - setattr(paragraph, field, value) + payload = _normalize_paragraph_payload(p_in.model_dump()) + payload.pop("id", None) + for field, value in payload.items(): + if value is not None: + setattr(paragraph, field, value) else: - paragraph = AnonymizationParagraph(**p_in.model_dump()) + payload = _normalize_paragraph_payload(p_in.model_dump()) + paragraph = AnonymizationParagraph(**payload) session.add(paragraph) session.commit() diff --git a/aymurai/meta/api_interfaces.py b/aymurai/meta/api_interfaces.py index 1ff51443..9fd601ce 100644 --- a/aymurai/meta/api_interfaces.py +++ b/aymurai/meta/api_interfaces.py @@ -1,9 +1,12 @@ import uuid from pydantic import UUID5, BaseModel, Field, RootModel +from typing import Literal from aymurai.meta.entities import EntityAttributes +from functools import cached_property + class SuccessResponse(BaseModel): id: int | uuid.UUID | None = None @@ -42,10 +45,18 @@ class DocumentInformation(BaseModel): labels: list[DocLabel] = Field(default_factory=list) +class LabelPolicy(BaseModel): + """Per-label policy for disambiguation and anonymization.""" + + disambiguation: Literal["none", "fuzzy", "llm"] | None = None + anonymize: bool | None = None + + class DocumentAnnotations(BaseModel): """Datatype for document annotations""" data: list[DocumentInformation] + label_policies: dict[str, LabelPolicy] | None = None class DataPublicDocumentAnnotations(RootModel): @@ -59,3 +70,20 @@ class Document(BaseModel): document_id: UUID5 header: list[str] | None = None footer: list[str] | None = None + + +class PromptSet(BaseModel): + label: str + system: str + user: str + + +class PromptLibrary(RootModel): + root: list[PromptSet] = Field(default_factory=list) + + @cached_property + def as_dict(self) -> dict[str, PromptSet]: + return {p.label: p for p in self.root} + + def get(self, label: str) -> PromptSet | None: + return self.as_dict.get(label) diff --git a/aymurai/meta/entities.py b/aymurai/meta/entities.py index 08eca7ae..871ee462 100644 --- a/aymurai/meta/entities.py +++ b/aymurai/meta/entities.py @@ -32,6 +32,20 @@ class EntityAttributes(BaseModel): description="Method used on the prediction label", ) aymurai_score: float | None = Field(None, description="Score for prediction") + aymurai_label_instance: int | None = Field( + None, + description="Label instance index assigned by order of appearance (e.g., 1, 2, 3).", + ) + aymurai_disambiguation: str | None = Field( + None, + description=( + "Override disambiguation mode for this entity (none, fuzzy, llm)." + ), + ) + aymurai_anonymize: bool | None = Field( + None, + description="Whether this entity should be anonymized in output.", + ) canonical_entity_id: UUID | None = Field( None, description="Reference to the canonical entity ID" ) @@ -103,10 +117,6 @@ class CanonicalEntity(BaseModel): attributes: dict[str, Any] = Field( default_factory=dict, description="Additional metadata for the entity" ) - relations: list[EntityRelation] = Field( - default_factory=list, - description="References to relations involving this entity", - ) @model_validator(mode="after") def validate_entity_id(self) -> CanonicalEntity: diff --git a/aymurai/settings.py b/aymurai/settings.py index 9fdd5331..cfaee728 100644 --- a/aymurai/settings.py +++ b/aymurai/settings.py @@ -1,3 +1,4 @@ +import json import os from pathlib import Path @@ -61,6 +62,32 @@ def assemble_cors_origins(cls, v) -> list[str]: LIBREOFFICE_BIN: str = "libreoffice" + # Disambiguation Config + + # Fuzzy Matching + THRESHOLD: int = 70 + + # LLM + MODEL: str = "phi4:14b" + MODEL_CONTEXT: int = 9500 + TEMPERATURE: float = 0.0 + CONTEXT_WINDOW_LENGTH: int | None = 120 + TOKEN_LIMIT_FRAC: float = 2 / 3 + TOKENIZER_MODEL: str = "microsoft/phi-4" + DECOMPOSE_BY: int | None = None + + # Label policies (JSON dict: label -> {disambiguation, anonymize}) + DISAMBIGUATION_LABEL_POLICIES: dict | None = None + + @field_validator("DISAMBIGUATION_LABEL_POLICIES", mode="before") + @classmethod + def parse_label_policies(cls, v): + if v is None or v == "": + return None + if isinstance(v, str): + return json.loads(v) + return v + load_env() settings = Settings() diff --git a/aymurai/transforms/datetime_formatter/__init__.py b/aymurai/transforms/datetime_formatter/__init__.py index e69de29b..822a1d04 100644 --- a/aymurai/transforms/datetime_formatter/__init__.py +++ b/aymurai/transforms/datetime_formatter/__init__.py @@ -0,0 +1 @@ +from .core import DatetimeFormatter diff --git a/aymurai/transforms/datetime_formatter/core.py b/aymurai/transforms/datetime_formatter/core.py index 8f3c49c9..fbdab7e1 100644 --- a/aymurai/transforms/datetime_formatter/core.py +++ b/aymurai/transforms/datetime_formatter/core.py @@ -55,7 +55,9 @@ def process(self, ent): text_repr = datetime.strftime("%d/%m/%Y") suggestions.append(text_repr) - ent["attrs"]["aymurai_label_subclass"] = suggestions + ent["attrs"]["aymurai_label_subclass"] = ( + [max(suggestions)] if suggestions else [] + ) return ent diff --git a/aymurai/transforms/datetime_formatter/patterns.py b/aymurai/transforms/datetime_formatter/patterns.py index 77bf1d9d..242b04e4 100644 --- a/aymurai/transforms/datetime_formatter/patterns.py +++ b/aymurai/transforms/datetime_formatter/patterns.py @@ -10,7 +10,13 @@ r"%d/%m/%y", r"(?i)%-d de %B del? %Y", r"(?i)%-d de %B %Y", + r"(?i)%-d %B de %Y", + r"(?i)%-d de %B %Y", + r"%d-%m-%Y", + r"%Y-%m-%d", + r"(?i)%-d de %B", ] + HOURS = [ r"%H[\.:]%M", r"(?i)%-H[\.:]%M horas", @@ -19,6 +25,7 @@ patterns = { "FECHA_RESOLUCION": DATES, + "FECHA": DATES, "HORA_DE_INICIO": HOURS, "HORA_DE_CIERRE": HOURS, } diff --git a/aymurai/utils/entity_disambiguation/__init__.py b/aymurai/utils/entity_disambiguation/__init__.py new file mode 100644 index 00000000..0794d1b3 --- /dev/null +++ b/aymurai/utils/entity_disambiguation/__init__.py @@ -0,0 +1,21 @@ +from aymurai.utils.entity_disambiguation.core import ( + assign_label_instances, + map_canonical_entities_ner_preds, +) +from aymurai.utils.entity_disambiguation.fuzzy import ( + build_canonical_entities, +) +from aymurai.utils.entity_disambiguation.llm import ( + llm_canonical_entities_inference, + load_prompts_from_yaml, +) +from aymurai.utils.entity_disambiguation.date_formatter import get_canonical_dates + +__all__ = [ + "assign_label_instances", + "build_canonical_entities", + "llm_canonical_entities_inference", + "map_canonical_entities_ner_preds", + "load_prompts_from_yaml", + "get_canonical_dates", +] diff --git a/aymurai/utils/entity_disambiguation/core.py b/aymurai/utils/entity_disambiguation/core.py new file mode 100644 index 00000000..085d94de --- /dev/null +++ b/aymurai/utils/entity_disambiguation/core.py @@ -0,0 +1,128 @@ +import copy +import uuid + +from aymurai.meta.api_interfaces import DocumentAnnotations +from aymurai.meta.entities import CanonicalEntities + + +def assign_label_instances( + predictions: DocumentAnnotations, +) -> DocumentAnnotations: + """ + Assigns ordered label instance indices (e.g., 1, 2) by appearance. + + Args: + predictions (DocumentAnnotations): Predictions with canonical IDs assigned. + + Returns: + DocumentAnnotations: Updated predictions with `aymurai_label_instance`. + """ + predictions_with_instances = copy.deepcopy(predictions) + + next_index_by_label: dict[str, int] = {} + instance_by_label_and_id: dict[tuple[str, str], int] = {} + + for document in predictions_with_instances: + if not document.labels: + continue + + for label in document.labels: + label_name = label.attrs.aymurai_label + entity_id = label.attrs.canonical_entity_id + + if not label_name or entity_id is None: + continue + + key = (label_name, str(entity_id)) + if key not in instance_by_label_and_id: + next_index_by_label[label_name] = ( + next_index_by_label.get(label_name, 0) + 1 + ) + instance_by_label_and_id[key] = next_index_by_label[label_name] + + label.attrs.aymurai_label_instance = instance_by_label_and_id[key] + + return predictions_with_instances + + +def map_canonical_entities_ner_preds( + predictions: DocumentAnnotations, + canonical_entities: CanonicalEntities, + *, + include_label_instances: bool = True, + force_labels: set[str] | None = None, +) -> DocumentAnnotations: + """ + Applies canonical entity IDs and roles back onto NER predictions. + + Args: + predictions (DocumentAnnotations): Original predictions to update. + canonical_entities (CanonicalEntities): Canonical entities with IDs/roles. + include_label_instances (bool): Whether to assign ordered label instance + indices (e.g., 1, 2). Defaults to True. + force_labels (set[str] | None): Labels to remap even if a canonical ID + already exists. + + Returns: + DocumentAnnotations: Updated predictions with canonical IDs, roles, and + optionally `aymurai_label_instance`. + """ + predictions_mapped = copy.deepcopy(predictions) + force_labels = force_labels or set() + + new_ids_map = {} + + for document in predictions_mapped: + if not document.labels: + continue + + for label in document.labels: + if not label.attrs: + continue + + if label.attrs.aymurai_label_subclass is None: + label.attrs.aymurai_label_subclass = [] + + force_remap = label.attrs.aymurai_label in force_labels + if force_remap: + label.attrs.canonical_entity_id = None + label.attrs.aymurai_label_subclass = [] + + if ( + label.attrs.canonical_entity_id is None + and len(label.attrs.aymurai_label_subclass) == 0 + ): + for ce in canonical_entities: + if label.attrs.aymurai_label == ce.aymurai_label: + entity_id = ce.entity_id + attributes = ce.attributes or {} + role = attributes.get("role") + aliases = ce.aliases + + clean_aliases = [str(a).strip().lower() for a in aliases] + label_text = ( + str(label.attrs.aymurai_alt_text or label.text) + .strip() + .lower() + ) + + if label_text in clean_aliases: + label.attrs.canonical_entity_id = entity_id + if ce.aymurai_label == "PER" and role is not None: + label.attrs.aymurai_label_subclass.append(role) + break + + if label.attrs.canonical_entity_id is None: + key = ( + label.attrs.aymurai_label, + str(label.attrs.aymurai_alt_text).strip(), + ) + if key not in new_ids_map: + new_ids_map[key] = uuid.uuid4() + + label.attrs.canonical_entity_id = new_ids_map[key] + + if include_label_instances: + return assign_label_instances(predictions_mapped) + + return predictions_mapped diff --git a/aymurai/utils/entity_disambiguation/date_formatter.py b/aymurai/utils/entity_disambiguation/date_formatter.py new file mode 100644 index 00000000..92e38aea --- /dev/null +++ b/aymurai/utils/entity_disambiguation/date_formatter.py @@ -0,0 +1,45 @@ +from aymurai.meta.api_interfaces import DocLabel +from aymurai.meta.entities import CanonicalEntity + + +def get_canonical_dates(labels: list[DocLabel]) -> list[CanonicalEntity]: + """ + Groups date labels by their normalized day and month (if available) to create canonical entities. + + Args: + labels (list[DocLabel]): A list of document labels to process. + + Returns: + list[CanonicalEntity]: A list of canonical entities representing unique dates, + each with its aliases and attributes. + """ + groups = {} + + for label in labels: + if label.attrs.aymurai_label != "FECHA": + continue + + raw_date = label.attrs.aymurai_alt_text or label.text + + norm_date = ( + max(label.attrs.aymurai_label_subclass) + if label.attrs.aymurai_label_subclass + else None + ) + + day_month_key = norm_date[:5] if norm_date is not None else norm_date + + if day_month_key not in groups: + groups[day_month_key] = CanonicalEntity( + aymurai_label="FECHA", + canonical_text=raw_date, + aliases=[], + attributes={}, + ) + + if day_month_key in groups and raw_date not in groups[day_month_key].aliases: + groups[day_month_key].aliases.append(raw_date) + + label.attrs.canonical_entity_id = groups[day_month_key].entity_id + + return list(groups.values()) diff --git a/aymurai/utils/entity_disambiguation/fuzzy.py b/aymurai/utils/entity_disambiguation/fuzzy.py new file mode 100644 index 00000000..71ceed76 --- /dev/null +++ b/aymurai/utils/entity_disambiguation/fuzzy.py @@ -0,0 +1,199 @@ +from collections import Counter +from typing import Iterable + +from rapidfuzz import process +from rapidfuzz.fuzz import token_set_ratio + +from aymurai.meta.api_interfaces import DocLabel +from aymurai.meta.entities import CanonicalEntity + + +def _find_parent(parent: list[int], idx: int) -> int: + """ + Finds the root representative of a union-find set with path compression. + + Args: + parent (list[int]): Parent pointers for the union-find structure. + idx (int): Index to resolve to its root. + + Returns: + int: The root index for the provided `idx`. + """ + if parent[idx] == idx: + return idx + parent[idx] = _find_parent(parent, parent[idx]) + return parent[idx] + + +def _union_parent(parent: list[int], left: int, right: int) -> None: + """ + Unions two indices in a union-find structure. + + Args: + parent (list[int]): Parent pointers for the union-find structure. + left (int): First index to union. + right (int): Second index to union. + + Returns: + None + """ + root_left, root_right = _find_parent(parent, left), _find_parent(parent, right) + if root_left != root_right: + parent[root_right] = root_left + + +def _cluster_aliases_with_cdist( + *, + items: list[dict[str, str]], + threshold: int, +) -> list[list[tuple[str, str, str]]]: + """ + Clusters alias items using RapidFuzz pairwise similarity with union-find. + + Args: + items (list[dict[str, str]]): Items containing "text" and "aymurai_label". + threshold (int): Minimum similarity threshold for clustering. + + Returns: + list[list[tuple[str, str, str]]]: Clusters of (original, normalized, label). + """ + if not items: + return [] + + entities = [item.get("text", "") for item in items] + labels = [item.get("aymurai_label", "UNKNOWN") for item in items] + + normed = [str(e).lower().strip() if e else "" for e in entities] + + sim = process.cdist(normed, normed, scorer=token_set_ratio, score_cutoff=threshold) + + parent = list(range(len(normed))) + + n = len(normed) + for i in range(n): + for j in range(i + 1, n): + if sim[i][j] >= threshold: + _union_parent(parent, i, j) + + clusters_map: dict[int, list[tuple[str, str, str]]] = {} + for idx in range(n): + root = _find_parent(parent, idx) + clusters_map.setdefault(root, []).append( + (entities[idx], normed[idx], labels[idx]) + ) + + return list(clusters_map.values()) + + +def _parse_cluster_item(item: tuple[str, str, str]) -> tuple[str, str, str]: + """ + Reorders a tuple from (original, normalized, label) to (label, original, normalized). + + Args: + item (tuple[str, str, str]): Tuple in the form (original, normalized, label). + + Returns: + tuple[str, str, str]: Tuple in the form (label, original, normalized). + """ + orig, norm, label = item + return label, orig, norm + + +def _pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str: + """ + Selects the most frequent label from a cluster. + + Args: + parsed_items (list[tuple[str, str, str]]): Items in (label, original, normalized). + + Returns: + str: The most common label in the cluster. + """ + labels = [label for label, _, _ in parsed_items] + return Counter(labels).most_common(1)[0][0] + + +def _pick_longest_alias(parsed_items: list[tuple[str, str, str]]) -> str: + """ + Picks a canonical text representation from a cluster. + + Args: + parsed_items (list[tuple[str, str, str]]): Items in (label, original, normalized). + + Returns: + str: The longest alias text in the cluster. + """ + return max(parsed_items, key=lambda item: len(item[1]))[1] + + +def _clusters_to_canonical_entities( + clusters: list[list[tuple[str, str, str]]], +) -> list[CanonicalEntity]: + """ + Converts clustered alias groups into CanonicalEntity objects. + + Args: + clusters (list[list[tuple[str, str, str]]]): Clusters of + (original, normalized, label). + + Returns: + list[CanonicalEntity]: Canonical entities derived from clusters. + """ + canonical_entities = [] + for cluster in clusters: + parsed = [_parse_cluster_item(item) for item in cluster] + label = _pick_cluster_label(parsed) + canonical_text = _pick_longest_alias(parsed) + aliases = sorted({orig for _, orig, _ in parsed}) + canonical_entities.append( + CanonicalEntity( + aymurai_label=label, + canonical_text=canonical_text, + aliases=aliases, + attributes={}, + ) + ) + return canonical_entities + + +def build_canonical_entities( + labels: Iterable[DocLabel], + *, + target_labels: set[str] | None = None, + threshold: int, +) -> list[CanonicalEntity]: + """ + Builds canonical entities by clustering label aliases per entity type. + + Args: + labels (Iterable[DocLabel]): NER labels to cluster. + target_labels (set[str] | None): Optional label filter; if provided, + only these labels are clustered. + threshold (int): Minimum similarity threshold for clustering. + + Returns: + list[CanonicalEntity]: Canonical entities built from clustered aliases. + """ + grouped: dict[str, list[dict[str, str]]] = {} + for label in labels: + attrs = label.attrs + if not attrs or not attrs.aymurai_label: + continue + if target_labels and attrs.aymurai_label not in target_labels: + continue + alias = attrs.aymurai_alt_text or label.text + grouped.setdefault(attrs.aymurai_label, []).append( + {"text": alias, "aymurai_label": attrs.aymurai_label} + ) + + canonical_entities: list[CanonicalEntity] = [] + for items in grouped.values(): + clusters = _cluster_aliases_with_cdist( + items=items, + threshold=threshold, + ) + canonical_entities.extend(_clusters_to_canonical_entities(clusters)) + + canonical_entities = sorted(canonical_entities, key=lambda x: x.canonical_text) + + return canonical_entities diff --git a/aymurai/utils/entity_disambiguation/llm.py b/aymurai/utils/entity_disambiguation/llm.py new file mode 100644 index 00000000..f3cc3fb7 --- /dev/null +++ b/aymurai/utils/entity_disambiguation/llm.py @@ -0,0 +1,379 @@ +import copy +import json +from functools import lru_cache +from pathlib import Path + +from more_itertools import unique_everseen +from transformers import AutoTokenizer + +from aymurai.llm_providers import OllamaLLMProvider +from aymurai.meta.api_interfaces import ( + DocLabel, + DocumentAnnotations, + PromptLibrary, + PromptSet, +) +from aymurai.meta.entities import CanonicalEntities, CanonicalEntity +from aymurai.settings import settings +from aymurai.utils.json_data import get_pretty +from aymurai.utils.yaml_data import load_yaml + + +@lru_cache(maxsize=1) +def load_prompts_from_yaml(): + """ + Loads and caches LLM prompt templates from the resources directory. + + Returns: + PromptLibrary: The loaded prompt library. + """ + path = Path(settings.RESOURCES_BASEPATH) / "llm/entity_disambiguation.yaml" + data = load_yaml(file_path=str(path)) + + prompts = [] + for label, content in data.items(): + prompts.append( + PromptSet( + label=label, + system=content.get("system", ""), + user=content.get("user", ""), + ) + ) + return PromptLibrary(root=prompts) + + +def _extract_snippet(doc_text: str, label: DocLabel, window_length: int | None) -> str: + """ + Extracts and normalizes a text snippet around a label span. + + Args: + doc_text (str): Full document text. + label (DocLabel): Label containing span offsets. + window_length (int | None): Number of characters to include around + the label. If None, returns the full document text. + + Returns: + str: The extracted and normalized snippet. + """ + + if window_length is None: + return " ".join(doc_text.split()) + + start, end = label.start_char, label.end_char + window_start = max(0, start - window_length) + window_end = min(len(doc_text), end + window_length) + + # Expand to word boundaries to avoid truncating leading/trailing words. + while window_start > 0 and not doc_text[window_start - 1].isspace(): + window_start -= 1 + while window_end < len(doc_text) and not doc_text[window_end - 1].isspace(): + window_end += 1 + + snippet = doc_text[window_start:window_end] + return " ".join(snippet.split()) + + +def _iter_alias_snippets( + predictions: DocumentAnnotations, + *, + entity_label: str, + aliases_set: set[str], + window_length: int | None, +): + """ + Yields (alias, snippet) pairs for labels matching the target entity label. + + Args: + predictions (DocumentAnnotations): Document annotations to scan. + entity_label (str): Target NER label to match. + aliases_set (set[str]): Alias set to filter matches. + window_length (int | None): Window length for snippet extraction. + + Yields: + tuple[str, str]: (alias, snippet) pairs in appearance order. + """ + for pred in predictions: + if not pred.labels: + continue + doc_text = getattr(pred, "document", "") + + for label in pred.labels: + if label.attrs.aymurai_label != entity_label: + continue + + alias = label.attrs.aymurai_alt_text or label.text + if alias not in aliases_set: + continue + + snippet = _extract_snippet(doc_text, label, window_length) + yield alias, snippet + + +def _collect_entity_context( + predictions: DocumentAnnotations, + entity_label: str, + aliases: list[str], + window_length: int | None, + max_context_snippets: int | None = 5, +) -> list[str]: + """ + Collects unique context snippets for aliases, preserving appearance order. + + Args: + predictions (DocumentAnnotations): Document annotations to search within. + entity_label (str): NER label category to match (e.g., "PER"). + aliases (list[str]): Aliases to match against label text. + window_length (int | None): Characters to include around each match. + max_context_snippets (int | None): Max number of snippets to return. + If None, returns all unique snippets. Defaults to 5. + + Returns: + list[str]: Ordered, deduplicated context snippets. + """ + aliases_set = set(aliases) + + deduped_in_order = list( + unique_everseen( + _iter_alias_snippets( + predictions, + entity_label=entity_label, + aliases_set=aliases_set, + window_length=window_length, + ), + key=lambda item: item[1], + ) + ) + + # baseline in appearance order: first snippet per alias + seen_aliases: set[str] = set() + baseline = [ + snippet + for alias, snippet in deduped_in_order + if not (alias in seen_aliases or seen_aliases.add(alias)) + ] + + # remaining snippets in appearance order + baseline_set = set(baseline) + remaining = [ + snippet for _, snippet in deduped_in_order if snippet not in baseline_set + ] + + merged = baseline + remaining + + effective_limit = ( + None + if max_context_snippets is None + else max(max_context_snippets, len(baseline)) + ) + + return merged if effective_limit is None else merged[:effective_limit] + + +def _add_canonical_entities_context( + predictions: DocumentAnnotations, + entities: CanonicalEntities, + context_window_length: int | None = 120, + target_label: str | None = None, + max_snippets_per_entity: int | None = 5, +) -> CanonicalEntities: + """ + Attaches context snippets to each canonical entity based on predictions. + + Args: + predictions (DocumentAnnotations): Full document annotations. + entities (CanonicalEntities): Canonical entities to enrich with context. + context_window_length (int | None): Characters to include around each + mention. If None, uses the full document text. + target_label (str | None): Optional label filter to restrict extraction. + max_snippets_per_entity (int | None): Max snippets per entity. If None, + returns all unique snippets. + + Returns: + CanonicalEntities: Entities enriched with context snippets in attributes. + """ + + entities_with_context = copy.deepcopy(entities) + + for entity in entities_with_context: + if entity.attributes is None: + entity.attributes = {} + + current_target = target_label or entity.aymurai_label + + entity.attributes["context"] = _collect_entity_context( + predictions, + current_target, + list(entity.aliases), + context_window_length, + max_snippets_per_entity, + ) + + return entities_with_context + + +_TOKENIZER_CACHE = {} + + +def _get_model_tokens( + system_prompt: str, user_prompt: str, tokenizer_model: str +) -> int: + """ + Estimates token count for a system/user prompt using a HuggingFace tokenizer. + + Args: + system_prompt (str): System role prompt. + user_prompt (str): User role prompt. + tokenizer_model (str): HuggingFace tokenizer identifier or local path. + + Returns: + int: Approximate token count for the combined prompts. + """ + try: + if tokenizer_model not in _TOKENIZER_CACHE: + _TOKENIZER_CACHE[tokenizer_model] = AutoTokenizer.from_pretrained( + tokenizer_model + ) + + tokenizer = _TOKENIZER_CACHE[tokenizer_model] + + messages = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt}, + ] + + full_text = tokenizer.apply_chat_template(messages, tokenize=False) + tokens = tokenizer.encode(full_text) + + return len(tokens) + + except Exception: + # Fallback: simple word count approximation (one word ~ 1.4 tokens) + combined_text = f"{system_prompt} {user_prompt}" + word_count = len(combined_text.split()) + + approx_tokens = int(word_count * 1.4) + 20 + + return approx_tokens + + +def llm_canonical_entities_inference( + paragraphs: DocumentAnnotations, + canonical_entities_pre_cluster: CanonicalEntities, + system_prompt: str, + user_prompt_template: str, + model: str, + tokenizer_model: str, + target_label: str, + context_window_length: int | None = 120, + model_context: int = 9_500, + token_limit_frac: float = 2 / 3, + temperature: int = 0, + decompose_by: int | None = 0, +) -> CanonicalEntities: + """ + Refines pre-clustered entities into canonical forms using LLM inference. + + Args: + paragraphs (DocumentAnnotations): Annotated paragraphs containing labels. + canonical_entities_pre_cluster (CanonicalEntities): Pre-clustered entities. + system_prompt (str): System prompt for the LLM. + user_prompt_template (str): Template for user prompt formatting. + model (str): LLM model identifier. + tokenizer_model (str): Tokenizer identifier for token counting. + target_label (str): NER label to process (e.g., "PER"). + context_window_length (int | None): Context window length around mentions. + model_context (int): Max context window size for the LLM. + token_limit_frac (float): Fraction of model_context to use for batching. + temperature (int): Sampling temperature. + decompose_by (int | None): Batch size; if None, inferred. + + Returns: + CanonicalEntities: Canonical entities refined by the LLM. + """ + + canonical_entities_with_context = _add_canonical_entities_context( + predictions=paragraphs, + entities=canonical_entities_pre_cluster, + context_window_length=context_window_length, + target_label=target_label, + ) + + canonical_entities_prompt = [ + ce.model_dump(include={"canonical_text", "aliases", "attributes"}) + for ce in canonical_entities_with_context + ] + + token_limit = int(model_context * token_limit_frac) + + if decompose_by is None: + # NOTE: dynamically find the maximum batch size that fits the token limit + # by iteratively shrinking the candidate list. + current_batch_size = len(canonical_entities_prompt) + + while current_batch_size > 0: + test_batch = canonical_entities_prompt[:current_batch_size] + test_prompt = user_prompt_template.format( + canonical_entities=get_pretty(test_batch) + ) + + num_tokens = _get_model_tokens( + system_prompt=system_prompt, + user_prompt=test_prompt, + tokenizer_model=tokenizer_model, + ) + + if num_tokens <= token_limit: + decompose_by = current_batch_size + break + + current_batch_size -= 1 + + if not decompose_by: + # NOTE: returns None if even a single entity exceeds the token limit. + return None + + if decompose_by <= 0: + entity_batches = [canonical_entities_prompt] + else: + entity_batches = [ + canonical_entities_prompt[i : i + decompose_by] + for i in range(0, len(canonical_entities_prompt), decompose_by) + ] + + all_raw_outputs = [] + all_user_prompts = [] + + for batch_index, batch in enumerate(entity_batches): + user_prompt = user_prompt_template.format( + canonical_entities=get_pretty(batch), + ) + + all_user_prompts.append(user_prompt) + + len_tokens = _get_model_tokens( + system_prompt=system_prompt, + user_prompt=user_prompt, + tokenizer_model=tokenizer_model, + ) + if len_tokens > model_context: + # NOTE: skip batches that exceed physical context window to prevent LLM hallucination or crash. + continue + + provider = OllamaLLMProvider(model=model) + response = provider.generate( + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt}, + ], + options={"temperature": temperature, "num_ctx": model_context}, + format=CanonicalEntities.model_json_schema(), + ) + + batch_entities = json.loads(response.text).get("canonical_entities", []) + all_raw_outputs.extend(batch_entities) + + canonical_entities_llm = [ + CanonicalEntity.model_validate(e) for e in all_raw_outputs + ] + + return canonical_entities_llm diff --git a/notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb new file mode 100644 index 00000000..5cc52d55 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb @@ -0,0 +1,1713 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "64957296", + "metadata": {}, + "source": [ + "from previous notebook `06-pre-disambiguation-optimization` we found that the best hyperparameters combinations are:\n", + "- scorer: `token_set_ratio`\n", + "- score_cutoff: `70`\n", + "- processor: `light_normalizer`\n", + "\n", + "we now proceed to make the canonical entities disambiguation checked by an LLM and we give the task to the LLM to give a role to each canonical entity when it is related to a person." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb2b1f59", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "607ea227", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import os\n", + "import re\n", + "from more_itertools import unique_everseen\n", + "\n", + "from pathlib import Path\n", + "from typing import Iterable\n", + "import uuid\n", + "\n", + "import requests\n", + "\n", + "import numpy as np\n", + "\n", + "from aymurai.llm_providers import OllamaLLMProvider\n", + "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", + "from aymurai.utils.json_data import get_pretty, save_json, load_json\n", + "\n", + "import aymurai.evaluation.metrics as aymurai_metrics\n", + "from aymurai.utils.entity_disambiguation import build_canonical_entities\n", + "from aymurai.meta.api_interfaces import DocumentAnnotations" + ] + }, + { + "cell_type": "markdown", + "id": "7ec52f24", + "metadata": {}, + "source": [ + "## /document-extract endpoint output and new endpoint anonymizer/disambiguate" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7c3b672", + "metadata": {}, + "outputs": [], + "source": [ + "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8000\")\n", + "ENDPOINT_DOCUMENT_EXTRACT = f\"{API_BASE_URL}/misc/document-extract\"\n", + "\n", + "DATA_ROOT = Path(\n", + " os.getenv(\n", + " \"DOCUMENT_DATA_ROOT\", \"../../../resources/data/restricted/disambiguation-eval/files\"\n", + " )\n", + ")\n", + "GOLD_JSON_ROOT = Path(\n", + " os.getenv(\n", + " \"GOLD_JSON_ROOT\",\n", + " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted\",\n", + " )\n", + ")\n", + "\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", + "JSON_EXTENSION = {\".json\"}\n", + "\n", + "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", + "\n", + "print(f\"Target endpoint: {ENDPOINT_DOCUMENT_EXTRACT}\")\n", + "print(f\"Data root: {DATA_ROOT.resolve()}\")\n", + "\n", + "LIMIT = int(os.getenv(\"DISAMBIGUATION_DOC_LIMIT\", \"0\")) # 0 = no limit\n", + "TARGET_LABELS = os.getenv(\"DISAMBIGUATION_TARGET_LABELS\", \"PER,DIRECCION\")\n", + "TARGET_LABELS = [label.strip() for label in TARGET_LABELS.split(\",\") if label.strip()]\n", + "\n", + "FUZZY_THRESHOLD = int(os.getenv(\"DISAMBIGUATION_THRESHOLD\", \"70\"))\n", + "FUZZY_SCORER = os.getenv(\"DISAMBIGUATION_SCORER\", \"token_set_ratio\")\n", + "FUZZY_PROCESSOR = os.getenv(\"DISAMBIGUATION_PROCESSOR\", \"light_normalizer\")\n", + "\n", + "print(f\"API: {API_BASE_URL}\")\n", + "print(f\"Data root: {DATA_ROOT}\")\n", + "print(f\"Target labels: {TARGET_LABELS or 'ALL'}\")\n", + "print(\n", + " f\"Fuzzy params: scorer={FUZZY_SCORER}, threshold={FUZZY_THRESHOLD}, processor={FUZZY_PROCESSOR}\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18a31502", + "metadata": {}, + "outputs": [], + "source": [ + "if not DATA_ROOT.exists():\n", + " raise FileNotFoundError(\n", + " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", + " )\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "if LIMIT > 0:\n", + " documents = documents[:LIMIT]\n", + "print(f\"Discovered {len(documents)} documents.\")\n", + "\n", + "gold_jsons = discover_documents(GOLD_JSON_ROOT, JSON_EXTENSION)\n", + "if LIMIT > 0:\n", + " gold_jsons = gold_jsons[:LIMIT]\n", + "print(f\"Discovered {len(gold_jsons)} gold JSON files.\")" + ] + }, + { + "cell_type": "markdown", + "id": "dcaed02f", + "metadata": {}, + "source": [ + "## Functions for API endpoints usage" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "464e9132", + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.experiments.entity_disambiguation.runner import (\n", + " call_extraction_api as extract_document,\n", + ")\n", + "\n", + "def predict_paragraphs(paragraphs: list[str]) -> list[dict]:\n", + " endpoint = f\"{API_BASE_URL}/anonymizer/predict\"\n", + " predictions = []\n", + " for paragraph in paragraphs:\n", + " response = requests.post(endpoint, json={\"text\": paragraph}, timeout=60)\n", + " response.raise_for_status()\n", + " predictions.append(response.json())\n", + " return predictions" + ] + }, + { + "cell_type": "markdown", + "id": "f2aa5d15", + "metadata": {}, + "source": [ + "## Canonical Entity extraction with ***LLM inference***" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8220314e", + "metadata": {}, + "outputs": [], + "source": [ + "# Available models\n", + "models = [\n", + " \"phi4:14b\",\n", + " \"gpt-oss:20b\",\n", + " \"llama3\",\n", + " \"llama3.1:8b\",\n", + " \"gemma3:270m\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82f8d1a2", + "metadata": {}, + "outputs": [], + "source": [ + "# # Sanity check\n", + "# provider = OllamaLLMProvider(model=models[0])\n", + "# provider.generate(\"hola, ¿cómo estás?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f40ef8ed", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_1 = \"\"\"\n", + "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", + "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", + "\n", + "# Material de Trabajo\n", + "Para tu análisis, recibirás:\n", + "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", + "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", + "\n", + "# Tus Tareas\n", + "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", + "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", + "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", + "\n", + "# Reglas de Salida\n", + "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", + "- `canonical_text`: El nombre más completo y formal encontrado.\n", + "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Defensor/a de Cámara\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", + "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", + "\n", + "# Estructura de Respuesta (JSON)\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Alias Principal\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\n", + "# Ejemplo de Input\n", + "Contexto: \"...la declaración del Sr. Juan Carlos Ruiz ante este Juzgado. El imputado Ruiz negó los cargos. Por su parte, la Dra. Elena Sosa, fiscal de la causa, solicitó...\"\n", + "Entidades Candidatas:\n", + "[{\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Juan Carlos Ruiz\",\n", + " \"aliases\": [\n", + " \"Juan Carlos Ruiz\",\n", + " \"Ruiz\"\n", + " ]\n", + "},\n", + "{\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Elena Sosa\",\n", + " \"aliases\": [\n", + " \"Elena Sosa\"\n", + " ]\n", + "}]\n", + "\n", + "# Ejemplo de Output\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Juan Carlos Ruiz\",\n", + " \"aliases\": [\n", + " \"Juan Carlos Ruiz\",\n", + " \"Ruiz\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Imputado\"\n", + " }\n", + " },\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Elena Sosa\",\n", + " \"aliases\": [\n", + " \"Elena Sosa\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Fiscal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40c5f5ae", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_2 = \"\"\"\n", + "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", + "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", + "\n", + "# Material de Trabajo\n", + "Para tu análisis, recibirás:\n", + "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", + "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", + "\n", + "# Tus Tareas\n", + "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", + "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", + "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", + "\n", + "# Reglas de Salida\n", + "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", + "- `canonical_text`: El nombre más completo y formal encontrado.\n", + "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", + " * \"Denunciante\"\n", + " * \"Denunciado/a\"\n", + " * \"Víctima\"\n", + " * \"Juez/a\"\n", + " * \"Defensor/a de Cámara\"\n", + " * \"Fiscal\"\n", + " * \"Abogado/a\"\n", + " * \"Perito/a\"\n", + " * \"Testigo\"\n", + " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", + "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", + "\n", + "# Estructura de Respuesta (JSON)\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Alias Principal\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3f2848b", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template_1 = \"\"\"\n", + "A continuación se proporcionan fragmentos relevantes de un documento judicial y una lista de entidades (personas) pre-clusterizadas por similitud de texto.\n", + "\n", + "# Contexto de la causa, párrafos con alguna mención a las personas:\n", + "{document_text}\n", + "\n", + "# Entidades Pre-clusterizadas a validar:\n", + "{canonical_entities}\n", + "\n", + "# Instrucciones:\n", + "1. **Validación de Aliases:** Analiza si los aliases dentro de cada entidad canónica pertenecen realmente a la misma persona según los párrafos de contexto. \n", + " - Si un alias pertenece a una persona distinta (ej. un familiar con nombre similar), sepáralo en una nueva entidad.\n", + " - Si dos grupos de entidades canónicas refieren a la misma persona, fusiónalos.\n", + "2. **Identificación de Roles:** Extrae el rol procesal de cada persona basándote en el contexto (ej: Juez/a, Fiscal, Denunciante, Imputado/a, Víctima, Abogado/a, Perito/a, Testigo).\n", + "3. **Normalización:** Define el `canonical_text` como el nombre más completo y formal que aparezca en los aliases, si este trabajo ya está bien hecho no lo modifiques.\n", + "4. **Formato:** Devuelve la lista final de entidades en el formato JSON solicitado en el system prompt, asegurándote de no omitir a nadie que sea relevante.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d9365ca", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_3 = \"\"\"\n", + "Eres un asistente experto en desambiguación de entidades judiciales.\n", + "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", + "\n", + "### Tu Tarea:\n", + "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", + "2. **Limpiar Aliases:** Elimina prefijos (Dr., Sra., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", + "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", + "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + " - **Nota:** No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica rol, usa `null`.\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve exclusivamente un array de objetos:\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Alias Principal\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f7bf0f7", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_4 = \"\"\"\n", + "Eres un asistente experto en desambiguación de entidades judiciales.\n", + "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", + "\n", + "### Tu Tarea:\n", + "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", + "2. **Limpiar Aliases:** Elimina prefijos (Dr., Dra., Dres., Sra., Sr., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", + "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", + "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + " \n", + " **Instrucciones críticas de rol:**\n", + " - Si una persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y el contexto no indica que es Juez, Fiscal o Defensor oficial, asígnale el rol de **\"Abogado/a\"**.\n", + " - No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica un rol de la lista, usa `null`.\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve exclusivamente un array de objetos:\n", + "[\n", + " {\n", + " \"canonical_text\": \"Nombre Completo Normalizado\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9bed0ca", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_5 = \"\"\"\n", + "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", + "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", + "4. **Limpiar:** En `canonical_text` y `aliases`, elimina otras palabras que no sean nombres propios.\n", + "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ac77829", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_6 = \"\"\"\n", + "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", + "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", + "4. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", + "5. No modifiques los `canonical_text` ni los `aliases` originales.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26f1b81b", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_7 = \"\"\"\n", + "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", + "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", + "4. **Manejo de Siglas e Iniciales:** Ten en cuenta que las personas pueden ser mencionadas por sus iniciales (ej: \"M.L.\" para \"Martín López\"). Si el contexto permite confirmar que unas iniciales refieren a una persona ya identificada, trátalas como un alias de esa misma entidad.\n", + "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona (ya sea por nombre completo, apellido solo o iniciales), devuélvelas como una sola entidad unificada.\n", + "6. **Integridad:** No modifiques los `canonical_text` ni los `aliases` originales; mantén la literalidad de lo extraído por el NER.\n", + "\n", + "Devuelve exclusivamente un array de objetos:\n", + "[\n", + " {\n", + " \"canonical_text\": \"Nombre Completo Normalizado\",\n", + " \"aliases\": [\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol Procesal\"\n", + " }\n", + " }\n", + "]\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55aff26f", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_8 = \"\"\"\n", + "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", + " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", + " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", + "2. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", + "3. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", + "4. **Asignación de Rol:** Identifica el rol procesal basándote EXCLUSIVAMENTE en esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "5. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve un array de objetos con esta estructura:\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Nombre de la entidad,\n", + " \"aliases\": [\n", + " \"Nombre de la entidad\",\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol de la lista o null\"\n", + " }\n", + " }\n", + "]\n", + "No incluyas el campo 'context' en tu respuesta final.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "822e348e", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_9 = \"\"\"\n", + "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Asignación de Rol:** Identifica el rol procesal basándote en las ventanas de contexto dentro de cada entidad canónica en la parte de `attributes`['context']\n", + " Elija un rol EXCLUSIVAMENTE en esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "2. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", + "3. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", + " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", + " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", + "4. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", + "5. **Limpieza:** Limpiá los `aliases` y el `canonical_text` si los mismos tienen otras palabras, pero respeta que estén escritos de igual manera a que sus ventanas de contexto.\n", + " Te dejo un ejemplo de esta instrucción.\n", + " Vos recibís:\n", + " {\n", + " \"canonical_text\": \"por DREXLER, JORGE\",\n", + " \"aliases\": [\n", + " \"por DREXLER, JORGE\",\n", + " \"Jorge Drexler\"\n", + " ],\n", + " \"attributes\": {\n", + " \"context\": [\n", + " \"Fdo. por DREXLER, JORGE - JUEZ DE CÁMARA el mismo cita en el documento 56 que Juan es culpable\",\n", + " \"es acaso Jorge Drexler el juez designado para esta causa\"\n", + " ]\n", + " }\n", + " \n", + " Debés entregar:\n", + " {\n", + " \"canonical_text\": \"DREXLER, JORGE\",\n", + " \"aliases\": [\n", + " \"DREXLER, JORGE\",\n", + " \"Jorge Drexler\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Juez/a\"\n", + " }\n", + " \n", + "6. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve un array de objetos con esta estructura:\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Nombre de la entidad,\n", + " \"aliases\": [\n", + " \"Nombre de la entidad\",\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol de la lista o null\"\n", + " }\n", + " }\n", + "]\n", + "\n", + "No incluyas el campo 'context' en tu respuesta final.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc51a3ea", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_10 = \"\"\"\n", + "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", + " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", + " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", + "2. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", + "3. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", + "4. **Asignación de Rol:** Identifica el rol procesal basándote EXCLUSIVAMENTE en esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "5. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", + "6. **Limpieza:** No modifiques la literalidad de los `aliases` ni del `canonical_text`.\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve un array de objetos con esta estructura:\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Nombre de la entidad,\n", + " \"aliases\": [\n", + " \"Nombre de la entidad\",\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol de la lista o null\"\n", + " }\n", + " }\n", + "]\n", + "No incluyas el campo 'context' en tu respuesta final.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb600013", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_11 = \"\"\"\n", + "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y corregir una lista de personas pre-agrupadas basándote en su contexto.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Validación de Límites (Boundaries):** El modelo NER a veces incluye palabras adyacentes que no son parte del nombre (ej: \"por\", \"Fdo.\", \"Dres\", \"dijo\", \"con\"). Tu tarea es corregir los `aliases` y el `canonical_text` para que contengan ÚNICAMENTE el nombre de la persona, pero manteniendo la literalidad exacta (mayúsculas, tildes, comas) de cómo está escrito en el fragmento de `context`.\n", + "2. **Fusión y Desambiguación:** - Si dos grupos de entidades refieren a la misma persona física, fusiónalos.\n", + " - Si una persona aparece por sus iniciales (ej: \"M.L.\"), asóciala como alias del nombre completo si el contexto lo confirma.\n", + "3. **Filtrado:** Elimina entidades que no sean personas físicas.\n", + "4. **Asignación de Rol:** Usa exclusivamente: \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "5. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, el rol es \"Abogado/a\".\n", + "6. **Limpieza:** No modifiques la literalidad de los `aliases` ni del `canonical_text`.\n", + "\n", + "### Formato de Salida (JSON):\n", + "[\n", + " {\n", + " \"entity_id\": \"ID original\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Nombre exacto extraído del texto\",\n", + " \"aliases\": [\n", + " \"Nombre exacto extraído del texto (Igual al canonical)\",\n", + " \"Otros aliases exactos del texto...\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol de la lista o null\"\n", + " }\n", + " }\n", + "]\n", + "**IMPORTANTE:** El primer elemento de `aliases` debe ser idéntico al `canonical_text`. No incluyas el campo 'context' en la respuesta.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca7b8755", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_DIR = \"\"\"\n", + "Eres un auditor experto en desambiguación de domicilios y direcciones en documentos judiciales. Tu tarea es validar y consolidar una lista de direcciones pre-agrupadas basándote en su contexto.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Consolidación Geográfica:** - Si dos entidades refieren a la misma ubicación física, fusiónalas en una sola entidad. \n", + " - Considera que una dirección puede estar escrita de forma completa en un alias (ej: \"Av. Rivadavia 123, CABA\") y de forma abreviada en otro (ej: \"Rivadavia 123\"). \n", + " - Trata las variantes ortográficas o abreviaturas (Av. vs Avenida, Piso vs P., Dpto vs Departamento) como aliases de la misma ubicación.\n", + "2. **Filtrado:** Elimina entidades que no sean direcciones postales o domicilios físicos (ej: correos electrónicos, URLs, nombres de juzgados o menciones abstractas como \"el domicilio constituido\").\n", + "\n", + "### Formato de Salida (JSON):\n", + "[\n", + " {\n", + " \"entity_id\": \"ID original\",\n", + " \"aymurai_label\": \"DIRECCION\",\n", + " \"canonical_text\": \"Dirección más completa extraída del texto\",\n", + " \"aliases\": [\n", + " \"Dirección más completa (Igual al canonical)\",\n", + " \"Variante 1\",\n", + " \"Variante 2\"\n", + " ],\n", + " \"attributes\": []\n", + " }\n", + "]\n", + "**IMPORTANTE:** No incluyas el campo 'context' en la respuesta.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d9feb9f", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template_DIR= \"\"\"\n", + "Se proporciona la lista de entidades (direcciones) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su validez y si hay otras entidades de direcciones que corresponden a esa.\n", + "\n", + "# Entidades Pre-clusterizadas a validar:\n", + "{canonical_entities}\n", + "\n", + "Procesa la lista siguiendo las instrucciones de filtrado y fusión.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0d4ad36", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template_PER = \"\"\"\n", + "Se proporciona la lista de entidades (personas) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su rol y validez.\n", + "\n", + "# Entidades Pre-clusterizadas a validar:\n", + "{canonical_entities}\n", + "\n", + "Procesa la lista siguiendo las instrucciones de filtrado, fusión y asignación de roles.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efc48244", + "metadata": {}, + "outputs": [], + "source": [ + "def pre_cluster(\n", + " paragraphs: list[dict],\n", + " target_labels: list[str] = None,\n", + " threshold: int = 70,\n", + ") -> CanonicalEntities:\n", + "\n", + " predictions = DocumentAnnotations(data=paragraphs)\n", + "\n", + " labels = [\n", + " label for paragraph in predictions.data for label in (paragraph.labels or [])\n", + " ]\n", + "\n", + " target_set = {label.strip() for label in target_labels} if target_labels else None\n", + "\n", + " canonical_entities = build_canonical_entities(\n", + " labels,\n", + " target_labels=target_set,\n", + " threshold=threshold,\n", + " )\n", + "\n", + " return canonical_entities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1431d116", + "metadata": {}, + "outputs": [], + "source": [ + "import copy\n", + "\n", + "def add_canonical_entities_context(\n", + " predictions: list[dict], \n", + " entities: list[dict],\n", + " context_window_length: int | None = 120,\n", + " max_context_snippets: int | None = 5,\n", + ") -> list[dict]:\n", + " \"\"\"\n", + " Creates a deep copy of entities and adds context. \n", + " \n", + " Args:\n", + " predictions: List of prediction dictionaries from the API.\n", + " entities: List of canonical entities.\n", + " context_window_length: Length of the window around the label. If None, full paragraph is used.\n", + " target_label: The specific label to filter. If None, all labels are considered.\n", + " \"\"\"\n", + " # 1. Deep copy to protect original variable\n", + " entities_with_context = copy.deepcopy(entities)\n", + "\n", + " # 2. Process each entity\n", + " for entity in entities_with_context:\n", + " if 'attributes' not in entity or entity['attributes'] is None:\n", + " entity['attributes'] = {}\n", + " if 'context' not in entity['attributes']:\n", + " entity['attributes']['context'] = []\n", + " \n", + " aliases = [a for a in entity.get('aliases', [])]\n", + "\n", + " alias_to_snippets = {alias: [] for alias in aliases}\n", + " all_snippets_ordered = []\n", + "\n", + " # 3. Iterate through predictions\n", + " for pred in predictions:\n", + " if not pred.get('labels'):\n", + " continue\n", + "\n", + " doc_text = pred.get('document', '')\n", + " \n", + " for label in pred.get('labels', []):\n", + " if entity.get('aymurai_label') and label['attrs']['aymurai_label'] != entity.get('aymurai_label'):\n", + " continue\n", + "\n", + " alias_name = label['attrs']['aymurai_alt_text']\n", + " if alias_name not in aliases:\n", + " continue\n", + " \n", + " # Handle Window vs Full Paragraph\n", + " if context_window_length is None:\n", + " snippet = doc_text\n", + " else:\n", + " start = label['start_char']\n", + " end = label['end_char']\n", + "\n", + " window_start = max(0, start - context_window_length)\n", + " window_end = min(len(doc_text), end + context_window_length)\n", + "\n", + " # Expand to word boundaries to avoid truncating leading/trailing words.\n", + " while window_start > 0 and not doc_text[window_start - 1].isspace():\n", + " window_start -= 1\n", + " while window_end < len(doc_text) and not doc_text[window_end - 1].isspace():\n", + " window_end += 1\n", + "\n", + " snippet = doc_text[window_start:window_end]\n", + "\n", + " clean_snippet = \" \".join(snippet.split())\n", + "\n", + " if clean_snippet not in alias_to_snippets[alias_name]:\n", + " alias_to_snippets[alias_name].append(clean_snippet)\n", + " if clean_snippet not in all_snippets_ordered:\n", + " all_snippets_ordered.append(clean_snippet)\n", + "\n", + " baseline = []\n", + " for alias in aliases:\n", + " snips = alias_to_snippets.get(alias, [])\n", + " if snips:\n", + " baseline.append(snips[0])\n", + " \n", + " baseline = list(unique_everseen(baseline))\n", + " \n", + " remaining = [s for s in all_snippets_ordered if s not in baseline]\n", + " merged = baseline + remaining\n", + "\n", + " effective_limit = max(max_context_snippets, len(baseline))\n", + " entity['attributes']['context'] = merged[:effective_limit]\n", + "\n", + " return entities_with_context\n", + "\n", + "def validate_canonical_entities(\n", + " canonical_entities_raw: list[CanonicalEntity], target_label: str | None = None\n", + ") -> list[CanonicalEntity]:\n", + "\n", + " if target_label:\n", + " canonical_entities_val = [\n", + " e for e in canonical_entities_raw if e.aymurai_label == target_label\n", + " ]\n", + " else:\n", + " canonical_entities_val = canonical_entities_raw\n", + "\n", + " canonical_entities_val = [\n", + " CanonicalEntity.model_validate(canonical_entity)\n", + " for canonical_entity in canonical_entities_val\n", + " ]\n", + "\n", + " canonical_entities_val = [\n", + " entity.model_dump() | {\"entity_id\": uuid.uuid4()}\n", + " for entity in canonical_entities_val\n", + " ]\n", + "\n", + " return canonical_entities_val" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fded1133", + "metadata": {}, + "outputs": [], + "source": [ + "tokenizers = {\n", + " \"phi4:14b\": \"microsoft/phi-4\",\n", + " \"gpt-oss:20b\": \"EleutherAI/gpt-neox-20b\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f93c8263", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "def get_phi4_tokens(system_prompt, user_prompt, model):\n", + " \n", + " try:\n", + " tokenizer = AutoTokenizer.from_pretrained(tokenizers[model])\n", + " \n", + " messages = [\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ]\n", + " try:\n", + " full_text = tokenizer.apply_chat_template(\n", + " messages, \n", + " tokenize=False, \n", + " add_generation_prompt=True\n", + " )\n", + " except Exception:\n", + " full_text = f\"{system_prompt}\\n{user_prompt}\"\n", + " \n", + " tokens = tokenizer.encode(full_text)\n", + " return len(tokens)\n", + " \n", + " except Exception as e:\n", + " print(f\"Error loading tokenizer {tokenizers[model]}: {e}\")\n", + " return int(len((system_prompt + user_prompt).split()) * 1.4)\n", + "\n", + "\n", + "\n", + "def llm_infer_canonical_entities(\n", + " system_prompt: str,\n", + " user_prompt_template: str,\n", + " model: str,\n", + " document_path: Path,\n", + " gold_path: Path,\n", + " context_window_length: int | None = 120,\n", + " model_context: int = 9_500,\n", + " token_limit_frac: float = 2/3,\n", + " target_label: str = \"PER\",\n", + " temperature: int = 0,\n", + " decompose_by: int | None = 0,\n", + ") -> dict:\n", + " \n", + " \"\"\"\n", + " Infers canonical entities using an LLM by providing context windows \n", + " around detected entities and pre-clusterization results.\n", + " \"\"\"\n", + "\n", + " # 1. Filename cleaning\n", + " clean_base_name = document_path.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", + " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + "\n", + " print(f\"----- Processing document: {target_filename} -----\")\n", + "\n", + " # 2. Extract document text via API\n", + " session = requests.Session()\n", + " api_response = extract_document(\n", + " session,\n", + " file_path = Path(document_path),\n", + " endpoint=f\"{API_BASE_URL}/misc/document-extract\",\n", + " timeout_s=300,\n", + " )\n", + " \n", + " document_paragraphs = api_response.get(\"detail\", {}).get(\"document\")\n", + "\n", + " if not document_paragraphs:\n", + " raise ValueError(\"Document text is empty or not found for {target_filename}.\")\n", + "\n", + " # 3. Get the pre-clustered entities with context from API calls\n", + " predictions = predict_paragraphs(document_paragraphs)\n", + " \n", + " canonical_entities_pre_cluster = pre_cluster(\n", + " paragraphs=predictions,\n", + " target_labels=[target_label],\n", + " threshold=70,\n", + " )\n", + "\n", + " canonical_entities_pre_cluster = validate_canonical_entities(\n", + " canonical_entities_raw=canonical_entities_pre_cluster,\n", + " target_label=target_label\n", + " )\n", + "\n", + " canonical_entities_with_context = add_canonical_entities_context(\n", + " predictions=predictions,\n", + " entities=canonical_entities_pre_cluster,\n", + " context_window_length=context_window_length,\n", + " )\n", + "\n", + " # 4. Clean entities to save tokens\n", + " canonical_entities_prompt = [\n", + " {\n", + " k: v\n", + " for k, v in ce.items()\n", + " if k in (\"canonical_text\",\"aliases\",\"attributes\")\n", + " }\n", + " for ce in canonical_entities_with_context\n", + " ]\n", + "\n", + " token_limit = int(model_context*token_limit_frac)\n", + "\n", + " # 5. Find Optimal Batch Size if decompose_by is None\n", + " if decompose_by is None:\n", + " current_batch_size = len(canonical_entities_prompt)\n", + " print(f\"{'-' * 60}\")\n", + " print(\"Searching for optimal batch size...\")\n", + " print(f\"Starting with {current_batch_size} entities.\")\n", + " \n", + " while current_batch_size > 0:\n", + " # Test with the first N entities\n", + " test_batch = canonical_entities_prompt[:current_batch_size]\n", + " test_prompt = user_prompt_template.format(\n", + " canonical_entities=get_pretty(test_batch)\n", + " )\n", + " \n", + " num_tokens = get_phi4_tokens(system_prompt=system_prompt, user_prompt=test_prompt, model=model)\n", + " \n", + " if num_tokens <= token_limit:\n", + " decompose_by = current_batch_size\n", + " print(f\"{'-' * 60}\")\n", + " print(f\"Optimal batch size found: {decompose_by} entities ({num_tokens} tokens).\")\n", + " break\n", + " \n", + " current_batch_size -= 1\n", + " \n", + " if not decompose_by: # Safety check if even 1 entity is too large\n", + " print(\"\\nEven a single entity exceeds the token limit.\")\n", + " return None\n", + "\n", + " # 6. Prepare batches. If decompose_by is 0 or less, we put all entities in one single list (one batch)\n", + " if decompose_by <= 0:\n", + " entity_batches = [canonical_entities_prompt]\n", + " else:\n", + " entity_batches = [\n", + " canonical_entities_prompt[i : i + decompose_by]\n", + " for i in range(0, len(canonical_entities_prompt), decompose_by)\n", + " ]\n", + "\n", + " all_raw_outputs = []\n", + " all_user_prompts = []\n", + "\n", + " # 7. Iterate through batches\n", + " for batch_index, batch in enumerate(entity_batches):\n", + " print(f\"{'-' * 60}\")\n", + " print(f\"Processing batch {batch_index + 1}/{len(entity_batches)}.\")\n", + " \n", + " user_prompt = user_prompt_template.format(\n", + " canonical_entities=get_pretty(batch),\n", + " )\n", + " \n", + " all_user_prompts.append(user_prompt)\n", + "\n", + " # 7.A Token Validation\n", + " len_tokens = get_phi4_tokens(system_prompt=system_prompt, user_prompt=user_prompt, model=model)\n", + " if len_tokens > model_context:\n", + " print(f\"Batch {batch_index} too large ({len_tokens} tokens).\")\n", + " print(\"Skipping batch.\")\n", + " continue\n", + " else:\n", + " print(f\"Total batch tokens: {len_tokens} within model context ({model_context}).\")\n", + "\n", + " # 7.B LLM Inference\n", + " provider = OllamaLLMProvider(model=model)\n", + " response = provider.generate(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " options={\"temperature\": temperature, \"num_ctx\": model_context},\n", + " format=CanonicalEntities.model_json_schema(),\n", + " )\n", + "\n", + " # 7.C Parse and collect results\n", + " batch_entities = json.loads(response.text).get(\"canonical_entities\", [])\n", + " all_raw_outputs.extend(batch_entities)\n", + "\n", + "\n", + " canonical_entities_llm = validate_canonical_entities(\n", + " canonical_entities_raw=all_raw_outputs\n", + " )\n", + "\n", + " # 8. Load Gold Standard for reference\n", + " canonical_entities_gold = load_json(json_file_path=gold_path)\n", + "\n", + " print(f\"{'-' * 60}\")\n", + " print(f\"Summary for {target_filename}:\")\n", + " print(f\"- Pre-cluster entities: {len(canonical_entities_pre_cluster)}\")\n", + " print(f\"- LLM generated entities: {len(canonical_entities_llm)}\")\n", + " print(f\"- Gold standard entities: {len(canonical_entities_gold)}\")\n", + "\n", + " # 9. Map the canonical entities in the predictions documents te return the right format for the front-end\n", + "\n", + " predictions_llm = copy.deepcopy(predictions)\n", + " \n", + " new_ids_map = {}\n", + "\n", + " for document in predictions_llm:\n", + " if not document.get('labels'):\n", + " continue\n", + "\n", + " for label in document.get('labels'):\n", + " label_text = label['attrs']['aymurai_alt_text']\n", + " if (\n", + " label['attrs']['canonical_entity_id'] is None\n", + " and len(label['attrs']['aymurai_label_subclass']) == 0\n", + " ):\n", + " pred_label = label['attrs']['aymurai_label']\n", + " for ce in canonical_entities_llm:\n", + " ce_label = ce.get(\"aymurai_label\")\n", + " if pred_label == ce_label:\n", + " entity_id = ce.get(\"entity_id\")\n", + " attributes = ce.get(\"attributes\") or {}\n", + " role = attributes.get(\"role\")\n", + " aliases = ce.get(\"aliases\") or []\n", + "\n", + " if any(\n", + " str(alias).strip() == str(label_text).strip()\n", + " for alias in aliases\n", + " ):\n", + " label['attrs']['canonical_entity_id'] = entity_id\n", + " if ce_label == \"PER\" and role is not None:\n", + " label['attrs']['aymurai_label_subclass'].append(role)\n", + " break\n", + "\n", + " if label['attrs']['canonical_entity_id'] is None:\n", + " key = (label['attrs']['aymurai_label'], str(label_text).strip())\n", + " if key not in new_ids_map:\n", + " new_ids_map[key] = uuid.uuid4()\n", + "\n", + " label['attrs']['canonical_entity_id'] = new_ids_map[key]\n", + "\n", + " # 10. Return structured results\n", + " return {\n", + " \"target_filename\": target_filename,\n", + " \"pre_cluster_json\": canonical_entities_pre_cluster,\n", + " \"canonical_entities_with_context\": canonical_entities_with_context,\n", + " \"llm_output_json\": canonical_entities_llm,\n", + " \"gold_standard_json\": canonical_entities_gold,\n", + " \"system_prompt\": system_prompt,\n", + " \"user_prompts\": all_user_prompts,\n", + " \"len_tokens\": len_tokens,\n", + " \"predictions\": predictions_llm\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586b1ddd", + "metadata": {}, + "outputs": [], + "source": [ + "def get_document_paths(document_check: Path, pre_clusterization_root: Path) -> dict:\n", + " \"\"\"\n", + " Generates the pre-cluster and gold JSON paths for a given document.\n", + " \n", + " Args:\n", + " document_check (Path): The original document Path object.\n", + " pre_clusterization_root (Path): The root directory for pre-clusterization data.\n", + " \n", + " Returns:\n", + " dict: A dictionary containing the 'pre_cluster_path' and 'gold_path'.\n", + " \"\"\"\n", + " # 1. Prepare target filename\n", + " clean_base_name = document_check.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", + " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", + "\n", + " # 2. Define subdirectories\n", + " pre_cluster_dir = pre_clusterization_root / 'best-pre-clusterization-per'\n", + " gold_dir = pre_clusterization_root / 'gold-jsons'\n", + "\n", + " # 3. Build final paths\n", + " pre_cluster_json_path = pre_cluster_dir / f\"{target_filename}-ner-preds-pre-cluster.json\"\n", + " gold_json_path = gold_dir / f\"{target_filename}-canonical-entities-gold.json\"\n", + "\n", + " return {\n", + " \"target_filename\": target_filename,\n", + " \"pre_cluster_path\": pre_cluster_json_path,\n", + " \"gold_path\": gold_json_path\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f419ede", + "metadata": {}, + "outputs": [], + "source": [ + "PRE_CLUSTERIZATION_ROOT = GOLD_JSON_ROOT.parent / \"pre-clusterization\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90229978", + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "\n", + "def run_llm_grid_search(\n", + " models: list,\n", + " system_prompts: dict,\n", + " user_prompt_templates: dict,\n", + " context_window_lengths: list,\n", + " model_context_lengths: list,\n", + " temperatures: list,\n", + " token_limit_frac: float,\n", + " documents: list[Path],\n", + " target_label: str,\n", + " base_output_path: Path,\n", + " decompose_by: int | None = 0\n", + "):\n", + " \"\"\"\n", + " Runs a grid search over hyperparameters using nested loops.\n", + " \"\"\"\n", + "\n", + " # To keep track of all results and find the best\n", + " all_combinations_results = []\n", + "\n", + " for model in models:\n", + " for system_prompt_name, system_prompt in system_prompts.items():\n", + " for user_prompt_template_name, user_prompt_template in user_prompt_templates.items():\n", + " for context_window_length in context_window_lengths:\n", + " for model_context in model_context_lengths:\n", + " for temperature in temperatures:\n", + " \n", + " print(\n", + " f\"\\nRunning grid search with model: {model}, \"\n", + " f\"context_window_length: {context_window_length}, \"\n", + " f\"model_context: {model_context}, \"\n", + " f\"temperature: {temperature}\"\n", + " )\n", + "\n", + " # 1. Create a descriptive folder name with the cleaned names\n", + " combo_name = (\n", + " f\"model-{model}-{system_prompt_name}-\"\n", + " f\"{user_prompt_template_name}-context_window-{context_window_length}-\"\n", + " f\"model_context-{model_context}-temperature-{temperature}\"\n", + " )\n", + "\n", + " combo_dir = base_output_path / combo_name\n", + " combo_dir.mkdir(parents=True, exist_ok=True)\n", + "\n", + " metrics_results = {}\n", + " metrics_file_path = (\n", + " combo_dir / f\"evaluation_metrics_{target_label}.json\"\n", + " )\n", + "\n", + " # List to collect scores for this specific combination\n", + " current_combo_scores = []\n", + "\n", + " for document in documents:\n", + " \n", + " # Get document paths\n", + " paths = get_document_paths(document, PRE_CLUSTERIZATION_ROOT) # We use this because the gold_json is in an specific folder\n", + "\n", + " # Run LLM inference\n", + " llm_response_dict = llm_infer_canonical_entities(\n", + " system_prompt=system_prompt,\n", + " user_prompt_template=user_prompt_template,\n", + " model=model,\n", + " document_path=document,\n", + " gold_path=paths['gold_path'], # When adding this to the experiment runner, make sure to pass the correct gold path\n", + " context_window_length=context_window_length,\n", + " model_context=model_context,\n", + " target_label=target_label,\n", + " temperature=temperature,\n", + " decompose_by=decompose_by,\n", + " token_limit_frac=token_limit_frac\n", + " )\n", + " # Extract predictions\n", + " ce_llm = llm_response_dict['llm_output_json']\n", + " \n", + " # Validate and prepare for saving\n", + " ce_llm = [\n", + " CanonicalEntity.model_validate(entity)\n", + " for entity in ce_llm\n", + " ]\n", + "\n", + " ce_llm = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in ce_llm\n", + " ]\n", + "\n", + " # Save the result as the llm-pred.json\n", + " target_filename = llm_response_dict['target_filename']\n", + " llm_filename = f\"{target_filename}-llm-pred.json\"\n", + " llm_output_path = combo_dir / llm_filename\n", + "\n", + " save_json(\n", + " file_path=llm_output_path, json_data=ce_llm\n", + " )\n", + "\n", + " # Extract gold standard\n", + " ce_gold = llm_response_dict['gold_standard_json']\n", + " \n", + " # Validate and prepare for saving\n", + " ce_gold = [\n", + " CanonicalEntity.model_validate(entity)\n", + " for entity in ce_gold\n", + " ]\n", + "\n", + " ce_gold = [\n", + " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", + " for entity in ce_gold\n", + " ]\n", + "\n", + " len_llm = len(ce_llm)\n", + " len_gold = len(ce_gold)\n", + "\n", + " # Evaluate metrics\n", + " score, metrics = aymurai_metrics.evaluate_disambiguation(\n", + " gold_json=ce_gold,\n", + " pred_json=ce_llm,\n", + " target_label=target_label,\n", + " )\n", + " current_combo_scores.append(score)\n", + " doc_id = target_filename\n", + " metrics_results[doc_id] = {\n", + " \"label\": target_label,\n", + " \"metric_value\": score,\n", + " \"detailed_metrics\": metrics,\n", + " \"len_llm_predictions\": len_llm,\n", + " \"len_gold_standard\": len_gold\n", + " }\n", + "\n", + " # --- Calculate Average for this combination ---\n", + " avg_score = (\n", + " np.mean(current_combo_scores) if current_combo_scores else 0.0\n", + " )\n", + "\n", + " # Add average to the results file for this folder\n", + " final_output = {\n", + " \"average_combination_score\": avg_score,\n", + " \"document_details\": metrics_results,\n", + " }\n", + "\n", + " with open(metrics_file_path, \"w\") as f:\n", + " json.dump(final_output, f, indent=4)\n", + "\n", + " # Store combination info for final ranking\n", + " all_combinations_results.append(\n", + " {\n", + " \"name\": combo_name,\n", + " \"score\": avg_score,\n", + " \"params\": {\n", + " \"model\": model,\n", + " \"system_prompt\": system_prompt_name,\n", + " \"user_prompt_template\": user_prompt_template_name,\n", + " \"context_window_length\": context_window_length,\n", + " \"model_context\": model_context\n", + " },\n", + " }\n", + " )\n", + "\n", + " # Save all combinations results in a summary file\n", + " summary_file_path = (\n", + " base_output_path / f\"results_summary_{target_label}.json\"\n", + " )\n", + " with open(summary_file_path, \"w\") as f:\n", + " json.dump(all_combinations_results, f, indent=4)\n", + "\n", + " # --- Find the best combination ---\n", + " best_combo = max(all_combinations_results, key=lambda x: x[\"score\"])\n", + "\n", + " print(f\"\\nGrid Search for {target_label} completed.\")\n", + "\n", + " # We now copy the best combination folder to a new location with a standardized name\n", + " src = Path(base_output_path) / best_combo[\"name\"]\n", + " # Combine the parent destination path with the new folder name\n", + " new_name = f\"best-llm-pred-{target_label.lower()}\"\n", + " dest = Path(base_output_path.parent) / new_name\n", + " \n", + " try:\n", + " # copytree creates the destination directory with the 'new_name'\n", + " shutil.copytree(src, dest)\n", + " print(f\"Successfully copied '{src.name}' to '{dest}'\")\n", + " except FileExistsError:\n", + " print(f\"Error: A folder named '{new_name}' already exists in '{base_output_path.parent}'\")\n", + " except Exception as e:\n", + " print(f\"An error occurred: {e}\") \n", + "\n", + " return best_combo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5110dc60", + "metadata": {}, + "outputs": [], + "source": [ + "models = [\n", + " \"phi4:14b\",\n", + " \"gpt-oss:20b\",\n", + " \"llama3\",\n", + " \"llama3.1:8b\",\n", + " \"gemma3:270m\",\n", + "]\n", + "\n", + "system_prompts = {\n", + " \"system_prompt_1\": system_prompt_1,\n", + " \"system_prompt_2\": system_prompt_2,\n", + " \"system_prompt_3\": system_prompt_3,\n", + " \"system_prompt_4\": system_prompt_4,\n", + " \"system_prompt_5\": system_prompt_5,\n", + " \"system_prompt_6\": system_prompt_6,\n", + " \"system_prompt_7\": system_prompt_7,\n", + " \"system_prompt_8\": system_prompt_8,\n", + " \"system_prompt_9\": system_prompt_9,\n", + " \"system_prompt_10\": system_prompt_10,\n", + " \"system_prompt_11\": system_prompt_11\n", + "}\n", + "\n", + "\n", + "user_prompt_templates = {\n", + " \"user_prompt_template_PER\": user_prompt_template_PER,\n", + "}\n", + "\n", + "context_window_lengths = list(range(80, 200, 10))\n", + "\n", + "model_context_lengths = [7_500, 8_000, 8_500, 9_000, 9_500]\n", + "\n", + "temperatures = list(np.arange(0,0.3,0.1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7949d277", + "metadata": {}, + "outputs": [], + "source": [ + "document_check = documents[1]\n", + "paths = get_document_paths(document_check, PRE_CLUSTERIZATION_ROOT)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6ff4f5a", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict = llm_infer_canonical_entities(\n", + " system_prompt=system_prompts['system_prompt_9'],\n", + " user_prompt_template=user_prompt_templates['user_prompt_template_PER'],\n", + " model=models[0],\n", + " document_path=document_check,\n", + " gold_path=paths[\"gold_path\"],\n", + " context_window_length=120,\n", + " model_context=9_500,\n", + " token_limit_frac=2/3,\n", + " temperature=0,\n", + " target_label=\"PER\",\n", + " decompose_by=None\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60a2356a", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict = llm_infer_canonical_entities(\n", + " system_prompt=system_prompt_DIR,\n", + " user_prompt_template=user_prompt_template_DIR,\n", + " model=models[0],\n", + " document_path=document_check,\n", + " gold_path=paths[\"gold_path\"],\n", + " context_window_length=None,\n", + " model_context=9_500,\n", + " token_limit_frac=2/3,\n", + " temperature=0,\n", + " target_label=\"DIRECCION\",\n", + " decompose_by=None\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "690c940d", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict['predictions']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e8bcb07", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict['llm_output_json']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "135c1f36", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict['canonical_entities_with_context']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59f003a9", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict[\"pre_cluster_json\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22e29b34", + "metadata": {}, + "outputs": [], + "source": [ + "llm_response_dict[\"gold_standard_json\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2845f2f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(llm_response_dict[\"user_prompts\"][0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38e587f3", + "metadata": {}, + "outputs": [], + "source": [ + "print(llm_response_dict['system_prompt'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fe27195", + "metadata": {}, + "outputs": [], + "source": [ + "models = [\n", + " \"phi4:14b\",\n", + "]\n", + "\n", + "system_prompts = {\n", + " \"system_prompt_9\": system_prompt_9\n", + "}\n", + "\n", + "\n", + "user_prompt_templates = {\n", + " \"user_prompt_template_PER\": user_prompt_template_PER,\n", + "}\n", + "\n", + "context_window_lengths = [120]\n", + "\n", + "model_context_lengths = [9_500]\n", + "\n", + "temperatures = [0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a32b4244", + "metadata": {}, + "outputs": [], + "source": [ + "top_result = run_llm_grid_search(\n", + " models=models,\n", + " system_prompts=system_prompts,\n", + " user_prompt_templates=user_prompt_templates,\n", + " context_window_lengths=context_window_lengths,\n", + " model_context_lengths=model_context_lengths,\n", + " temperatures=temperatures,\n", + " documents=documents,\n", + " target_label=\"PER\",\n", + " base_output_path=GOLD_JSON_ROOT.parent / \"llm-grid-search-results\",\n", + " token_limit_frac=2/3,\n", + " decompose_by=None\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86da6a45", + "metadata": {}, + "outputs": [], + "source": [ + "print(\n", + " f\"The best hyperparameter combination is '{top_result['name']}' \"\n", + " f\"with an average score of {top_result['score']:.4f}.\"\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb deleted file mode 100644 index 6af18cea..00000000 --- a/notebooks/experiments/entity-disambiguation/07-PER-entities-disambiguation.ipynb +++ /dev/null @@ -1,1487 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "64957296", - "metadata": {}, - "source": [ - "from previous notebook `06-pre-disambiguation-optimization` we found that the best hyperparameters combinations are:\n", - "- scorer: `token_set_ratio`\n", - "- score_cutoff: `70`\n", - "- processor: `light_normalizer`\n", - "\n", - "we now proceed to make the canonical entities disambiguation checked by an LLM and we give the task to the LLM to give a role to each canonical entity when it is related to a person." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb2b1f59", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext rich\n", - "\n", - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "607ea227", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import json\n", - "import mimetypes\n", - "import os\n", - "import re\n", - "import time\n", - "import unicodedata\n", - "from collections import Counter\n", - "from operator import itemgetter\n", - "from pathlib import Path\n", - "from typing import Iterable, Tuple\n", - "\n", - "import requests\n", - "from tqdm import tqdm\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import requests\n", - "from more_itertools import flatten, unique_everseen\n", - "\n", - "\n", - "from aymurai.llm_providers import OllamaLLMProvider\n", - "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", - "from aymurai.utils.json_data import get_pretty, save_json, load_json\n", - "import aymurai.evaluation.metrics as aymurai_metrics\n", - "\n", - "from rapidfuzz.fuzz import token_set_ratio\n", - "from rapidfuzz import process" - ] - }, - { - "cell_type": "markdown", - "id": "1003fbb5", - "metadata": {}, - "source": [ - "# #**1** First Step: Prepare the ***gold_json*** and ***ner_preds_json***" - ] - }, - { - "cell_type": "markdown", - "id": "7ec52f24", - "metadata": {}, - "source": [ - "## /document-extract endpoint output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c7c3b672", - "metadata": {}, - "outputs": [], - "source": [ - "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://127.0.0.1:8000\")\n", - "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", - "DATA_ROOT = Path(\n", - " os.getenv(\n", - " \"DOCUMENT_DATA_ROOT\", \"../../../resources/data/restricted/disambiguation-eval/files\"\n", - " )\n", - ")\n", - "GOLD_JSON_ROOT = Path(\n", - " os.getenv(\n", - " \"GOLD_JSON_ROOT\",\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted\",\n", - " )\n", - ")\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "JSON_EXTENSION = {\".json\"}\n", - "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", - "\n", - "print(f\"Target endpoint: {ENDPOINT}\")\n", - "print(f\"Data root: {DATA_ROOT.resolve()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18a31502", - "metadata": {}, - "outputs": [], - "source": [ - "if not DATA_ROOT.exists():\n", - " raise FileNotFoundError(\n", - " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", - " )\n", - "\n", - "\n", - "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "\n", - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "print(f\"Discovered {len(documents)} documents.\")\n", - "\n", - "gold_jsons = discover_documents(GOLD_JSON_ROOT, JSON_EXTENSION)\n", - "print(f\"Discovered {len(gold_jsons)} gold JSON files.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "65a2d588", - "metadata": {}, - "outputs": [], - "source": [ - "def call_extraction_api(\n", - " session: requests.Session, file_path: Path\n", - ") -> dict[str, object]:\n", - " payload: dict[str, object] = {\n", - " \"path\": str(file_path),\n", - " \"status\": \"failure\",\n", - " \"status_code\": None,\n", - " \"elapsed_s\": None,\n", - " \"detail\": None,\n", - " }\n", - "\n", - " if not file_path.exists():\n", - " payload[\"detail\"] = \"File does not exist\"\n", - " return payload\n", - "\n", - " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", - " files = {\n", - " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", - " }\n", - "\n", - " try:\n", - " start = time.perf_counter()\n", - " response = session.post(\n", - " ENDPOINT,\n", - " files=files,\n", - " timeout=REQUEST_TIMEOUT_S,\n", - " )\n", - " elapsed = time.perf_counter() - start\n", - " except requests.RequestException as exc:\n", - " payload[\"detail\"] = f\"Request failed: {exc}\"\n", - " return payload\n", - " finally:\n", - " files[\"file\"][1].close()\n", - "\n", - " payload[\"status_code\"] = response.status_code\n", - " payload[\"elapsed_s\"] = elapsed\n", - "\n", - " try:\n", - " response_body = response.json()\n", - " except ValueError:\n", - " response_body = {\"raw\": response.text[:500]}\n", - "\n", - " if response.ok:\n", - " payload[\"status\"] = \"success\"\n", - " payload[\"detail\"] = {\n", - " \"document_id\": response_body.get(\"document_id\"),\n", - " \"document\": response_body.get(\"document\", []),\n", - " }\n", - " else:\n", - " payload[\"detail\"] = response_body\n", - "\n", - " return payload" - ] - }, - { - "cell_type": "markdown", - "id": "dcaed02f", - "metadata": {}, - "source": [ - "## Inference" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "464e9132", - "metadata": {}, - "outputs": [], - "source": [ - "from itertools import chain\n", - "from operator import itemgetter\n", - "from typing import Any\n", - "\n", - "from more_itertools import unique_everseen\n", - "\n", - "\n", - "# Function to make inference using the API\n", - "def get_predictions(sample: str) -> dict:\n", - " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample})\n", - " response.raise_for_status()\n", - " return response.json()\n", - "\n", - "\n", - "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", - " \"\"\"\n", - " Parse prediction labels to extract unique aymurai_label and aymurai_alt_text pairs.\n", - "\n", - " Args:\n", - " predictions (list[dict[str, Any]]): A list of prediction dictionaries.\n", - "\n", - " Returns:\n", - " list[dict[str, str]]: A list of dictionaries containing unique aymurai_label and aymurai_alt_text pairs.\n", - " \"\"\"\n", - " attrs_stream = (\n", - " label.get(\"attrs\") or {}\n", - " for label in chain.from_iterable(pred.get(\"labels\", ()) for pred in predictions)\n", - " )\n", - "\n", - " unique_pairs = unique_everseen(\n", - " (\n", - " attrs.get(\"aymurai_label\"),\n", - " attrs.get(\"aymurai_alt_text\"),\n", - " )\n", - " for attrs in attrs_stream\n", - " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", - " )\n", - "\n", - " return sorted(\n", - " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", - " key=itemgetter(\"aymurai_label\", \"text\"),\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "b690bfc0", - "metadata": {}, - "source": [ - "## ***gold_json*** and ***ner_preds_json*** for input to clusterization developement" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0b37a8ac", - "metadata": {}, - "outputs": [], - "source": [ - "def extract_and_save_entities(\n", - " document_paths: list[Path], output_path: Path, file_ending: str, target_label: str\n", - ") -> None:\n", - " \"\"\"\n", - " Processes a list of documents to extract specific entities and saves\n", - " each result as an individual JSON file.\n", - " It has been optimized to handle both document files and pre-existing JSON files.\n", - " \"\"\"\n", - "\n", - " with requests.Session() as session:\n", - " if \".json\" not in document_paths[0].suffix:\n", - " for doc_path in tqdm(\n", - " document_paths, desc=f\"Extracting {target_label} entities\"\n", - " ):\n", - " doc_path = Path(doc_path)\n", - "\n", - " # 1. API Extraction\n", - " response = call_extraction_api(session, doc_path)\n", - " document_data = response.get(\"detail\", {}).get(\"document\")\n", - "\n", - " if not document_data:\n", - " print(f\"Warning: No document content found for {doc_path.name}\")\n", - " continue\n", - "\n", - " # 2. Processing\n", - " raw_predictions = [\n", - " get_predictions(paragraph) for paragraph in document_data\n", - " ]\n", - " parsed_labels = parse_prediction_labels(raw_predictions)\n", - "\n", - " # 3. Filtering by dynamic label\n", - " filtered_entities = [\n", - " item\n", - " for item in parsed_labels\n", - " if item.get(\"aymurai_label\") == target_label\n", - " ]\n", - "\n", - " # 4. Saving individual file\n", - " clean_base_name = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", - " )\n", - " clean_base_name = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", - " clean_name = re.sub(r\"-{2,}\", \"-\", clean_base_name)\n", - " file_name = f\"{clean_name}{file_ending}\"\n", - " save_path = output_path / file_name\n", - "\n", - " save_json(file_path=save_path, json_data=filtered_entities)\n", - " else:\n", - " for doc_path in tqdm(\n", - " document_paths, desc=f\"Extracting {target_label} entities\"\n", - " ):\n", - " doc_path = Path(doc_path)\n", - "\n", - " # 1. Load existing JSON data\n", - " document_data = load_json(doc_path)\n", - "\n", - " if not document_data:\n", - " print(f\"Warning: No document content found for {doc_path.name}\")\n", - " continue\n", - "\n", - " # 2. Filtering by dynamic label\n", - " filtered_entities = [\n", - " item\n", - " for item in document_data\n", - " if item.get(\"aymurai_label\") == target_label\n", - " ]\n", - "\n", - " # 3. Saving individual file\n", - " clean_base_name = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", - " )\n", - " clean_base_name = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", - " clean_name = re.sub(r\"-{2,}\", \"-\", clean_base_name)\n", - " file_name = f\"{clean_name}{file_ending}\"\n", - " save_path = output_path / file_name\n", - "\n", - " save_json(file_path=save_path, json_data=filtered_entities)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4824a9c3", - "metadata": {}, - "outputs": [], - "source": [ - "def cluster_with_cdist(\n", - " items: list[dict],\n", - " threshold: int = 90,\n", - " scorer: callable = token_set_ratio,\n", - " processor: callable = None,\n", - "):\n", - " \"\"\"\n", - " Cluster entities and prepare them for CanonicalEntity conversion.\n", - " \"\"\"\n", - " if not items:\n", - " return []\n", - "\n", - " # 1. Extract texts and apply normalization\n", - " # We keep track of the original text, the processed text, and the label\n", - " entities = [item.get(\"text\", \"\") for item in items]\n", - " labels = [item.get(\"aymurai_label\", \"UNKNOWN\") for item in items]\n", - "\n", - " if processor:\n", - " normed = [processor(e) for e in entities]\n", - " else:\n", - " normed = [str(e) for e in entities]\n", - "\n", - " # 2. Similarity Matrix\n", - " sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold)\n", - " sim = np.array(sim)\n", - "\n", - " # 3. Union-Find Logic\n", - " parent = list(range(len(normed)))\n", - "\n", - " def find(i):\n", - " if parent[i] == i:\n", - " return i\n", - " parent[i] = find(parent[i])\n", - " return parent[i]\n", - "\n", - " def union(i, j):\n", - " root_i, root_j = find(i), find(j)\n", - " if root_i != root_j:\n", - " parent[root_j] = root_i\n", - "\n", - " n = len(normed)\n", - " for i in range(n):\n", - " for j in range(i + 1, n):\n", - " if sim[i, j] >= threshold:\n", - " union(i, j)\n", - "\n", - " # 4. Group into the format parse_item expects: (orig, norm, label)\n", - " clusters_map = {}\n", - " for idx in range(n):\n", - " root = find(idx)\n", - " if root not in clusters_map:\n", - " clusters_map[root] = []\n", - " # This tuple matches your 'parse_item' len == 3 condition\n", - " clusters_map[root].append((entities[idx], normed[idx], labels[idx]))\n", - "\n", - " return list(clusters_map.values())\n", - "\n", - "\n", - "def parse_item(item: tuple[str, ...]) -> tuple[str, str, str]:\n", - " \"\"\"\n", - " Parse an item into (label, orig, norm).\n", - "\n", - " Accepts:\n", - " - (orig, norm, label)\n", - " - (labelled_orig, labelled_norm) with prefix 'LABEL:'\n", - " Args:\n", - " item (tuple[str, ...]): input item\n", - "\n", - " Returns:\n", - " tuple[str, str, str]: (label, orig, norm)\n", - " \"\"\"\n", - " if len(item) == 3:\n", - " orig, norm, label = item\n", - " return label, orig, norm\n", - "\n", - " # len == 2: assume \"LABEL:text\"\n", - " labelled_orig, labelled_norm = item\n", - " label, orig = labelled_orig.split(\":\", 1)\n", - " _, norm = labelled_norm.split(\":\", 1)\n", - " return label, orig, norm\n", - "\n", - "\n", - "def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str:\n", - " \"\"\"\n", - " Pick the most common label from parsed items.\n", - "\n", - " Args:\n", - " parsed_items (list[tuple[str, str, str]]): parsed items\n", - "\n", - " Returns:\n", - " str: chosen label\n", - " \"\"\"\n", - " labels = [lbl for lbl, _, _ in parsed_items]\n", - " # majority vote; fallback to first\n", - " return Counter(labels).most_common(1)[0][0]\n", - "\n", - "\n", - "def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str:\n", - " \"\"\"\n", - " Choose the longest original surface form; tweak as needed.\n", - "\n", - " Args:\n", - " parsed_items (list[tuple[str, str, str]]): parsed items\n", - "\n", - " Returns:\n", - " str: chosen canonical text\n", - " \"\"\"\n", - " return max(parsed_items, key=lambda x: len(x[1]))[1]\n", - "\n", - "\n", - "def clusters_to_canonical_entities(\n", - " clusters: list[list[tuple[str, str]]],\n", - ") -> list[CanonicalEntity]:\n", - " \"\"\"\n", - " Convert clusters to CanonicalEntity objects.\n", - "\n", - " Args:\n", - " clusters (list[list[tuple[str, str]]]): clusters of (original, normalized) entity tuples\n", - "\n", - " Returns:\n", - " list[CanonicalEntity]: list of CanonicalEntity objects\n", - " \"\"\"\n", - " canonical_entities = []\n", - "\n", - " for cluster in clusters:\n", - " parsed = [parse_item(item) for item in cluster] # [(label, orig, norm), ...]\n", - " label = pick_cluster_label(parsed)\n", - " canonical_text = pick_canonical_text(parsed)\n", - " aliases = sorted({orig for _, orig, _ in parsed})\n", - " ce = CanonicalEntity(\n", - " aymurai_label=label,\n", - " canonical_text=canonical_text,\n", - " aliases=aliases,\n", - " attributes={},\n", - " relations=[],\n", - " )\n", - " canonical_entities.append(ce)\n", - "\n", - " return canonical_entities\n", - "\n", - "\n", - "def light_normalizer(s: str) -> str:\n", - " return s.lower().strip() if s else \"\"" - ] - }, - { - "cell_type": "markdown", - "id": "f2aa5d15", - "metadata": {}, - "source": [ - "## Canonical Entity extraction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8220314e", - "metadata": {}, - "outputs": [], - "source": [ - "# Available models\n", - "\n", - "models = [\n", - " \"phi4:14b\",\n", - " \"gpt-oss:20b\",\n", - " \"llama3\",\n", - " \"llama3.1:8b\",\n", - " \"gemma3:270m\",\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "82f8d1a2", - "metadata": {}, - "outputs": [], - "source": [ - "# Sanity check\n", - "provider = OllamaLLMProvider(model=models[0])\n", - "provider.generate(\"hola, ¿cómo estás?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f40ef8ed", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_1 = \"\"\"\n", - "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", - "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", - "\n", - "# Material de Trabajo\n", - "Para tu análisis, recibirás:\n", - "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", - "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", - "\n", - "# Tus Tareas\n", - "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", - "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", - "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", - "\n", - "# Reglas de Salida\n", - "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", - "- `canonical_text`: El nombre más completo y formal encontrado.\n", - "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Defensor/a de Cámara\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", - "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", - "\n", - "# Estructura de Respuesta (JSON)\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Alias Principal\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\n", - "# Ejemplo de Input\n", - "Contexto: \"...la declaración del Sr. Juan Carlos Ruiz ante este Juzgado. El imputado Ruiz negó los cargos. Por su parte, la Dra. Elena Sosa, fiscal de la causa, solicitó...\"\n", - "Entidades Candidatas:\n", - "[{\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Juan Carlos Ruiz\",\n", - " \"aliases\": [\n", - " \"Juan Carlos Ruiz\",\n", - " \"Ruiz\"\n", - " ]\n", - "},\n", - "{\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Elena Sosa\",\n", - " \"aliases\": [\n", - " \"Elena Sosa\"\n", - " ]\n", - "}]\n", - "\n", - "# Ejemplo de Output\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Juan Carlos Ruiz\",\n", - " \"aliases\": [\n", - " \"Juan Carlos Ruiz\",\n", - " \"Ruiz\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Imputado\"\n", - " }\n", - " },\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Elena Sosa\",\n", - " \"aliases\": [\n", - " \"Elena Sosa\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Fiscal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40c5f5ae", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_2 = \"\"\"\n", - "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", - "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", - "\n", - "# Material de Trabajo\n", - "Para tu análisis, recibirás:\n", - "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", - "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", - "\n", - "# Tus Tareas\n", - "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", - "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", - "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", - "\n", - "# Reglas de Salida\n", - "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", - "- `canonical_text`: El nombre más completo y formal encontrado.\n", - "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Defensor/a de Cámara\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", - "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", - "\n", - "# Estructura de Respuesta (JSON)\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Alias Principal\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3f2848b", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template_1 = \"\"\"\n", - "A continuación se proporcionan fragmentos relevantes de un documento judicial y una lista de entidades (personas) pre-clusterizadas por similitud de texto.\n", - "\n", - "# Contexto de la causa, párrafos con alguna mención a las personas:\n", - "{document_text}\n", - "\n", - "# Entidades Pre-clusterizadas a validar:\n", - "{canonical_entities}\n", - "\n", - "# Instrucciones:\n", - "1. **Validación de Aliases:** Analiza si los aliases dentro de cada entidad canónica pertenecen realmente a la misma persona según los párrafos de contexto. \n", - " - Si un alias pertenece a una persona distinta (ej. un familiar con nombre similar), sepáralo en una nueva entidad.\n", - " - Si dos grupos de entidades canónicas refieren a la misma persona, fusiónalos.\n", - "2. **Identificación de Roles:** Extrae el rol procesal de cada persona basándote en el contexto (ej: Juez/a, Fiscal, Denunciante, Imputado/a, Víctima, Abogado/a, Perito/a, Testigo).\n", - "3. **Normalización:** Define el `canonical_text` como el nombre más completo y formal que aparezca en los aliases, si este trabajo ya está bien hecho no lo modifiques.\n", - "4. **Formato:** Devuelve la lista final de entidades en el formato JSON solicitado en el system prompt, asegurándote de no omitir a nadie que sea relevante.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7d9365ca", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_3 = \"\"\"\n", - "Eres un asistente experto en desambiguación de entidades judiciales.\n", - "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", - "\n", - "### Tu Tarea:\n", - "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", - "2. **Limpiar Aliases:** Elimina prefijos (Dr., Sra., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", - "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", - "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - " - **Nota:** No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica rol, usa `null`.\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve exclusivamente un array de objetos:\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Alias Principal\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0f7bf0f7", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_4 = \"\"\"\n", - "Eres un asistente experto en desambiguación de entidades judiciales.\n", - "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", - "\n", - "### Tu Tarea:\n", - "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", - "2. **Limpiar Aliases:** Elimina prefijos (Dr., Dra., Dres., Sra., Sr., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", - "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", - "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - " \n", - " **Instrucciones críticas de rol:**\n", - " - Si una persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y el contexto no indica que es Juez, Fiscal o Defensor oficial, asígnale el rol de **\"Abogado/a\"**.\n", - " - No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica un rol de la lista, usa `null`.\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve exclusivamente un array de objetos:\n", - "[\n", - " {\n", - " \"canonical_text\": \"Nombre Completo Normalizado\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9bed0ca", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_5 = \"\"\"\n", - "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", - "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", - "4. **Limpiar:** En `canonical_text` y `aliases`, elimina otras palabras que no sean nombres propios.\n", - "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ac77829", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_6 = \"\"\"\n", - "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", - "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", - "4. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", - "5. No modifiques los `canonical_text` ni los `aliases` originales.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "26f1b81b", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_7 = \"\"\"\n", - "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", - "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", - "4. **Manejo de Siglas e Iniciales:** Ten en cuenta que las personas pueden ser mencionadas por sus iniciales (ej: \"M.L.\" para \"Martín López\"). Si el contexto permite confirmar que unas iniciales refieren a una persona ya identificada, trátalas como un alias de esa misma entidad.\n", - "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona (ya sea por nombre completo, apellido solo o iniciales), devuélvelas como una sola entidad unificada.\n", - "6. **Integridad:** No modifiques los `canonical_text` ni los `aliases` originales; mantén la literalidad de lo extraído por el NER.\n", - "\n", - "Devuelve exclusivamente un array de objetos:\n", - "[\n", - " {\n", - " \"canonical_text\": \"Nombre Completo Normalizado\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f3eec132", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template_2 = \"\"\"\n", - "A continuación se proporcionan fragmentos relevantes de un documento judicial y una lista de entidades (personas) pre-clusterizadas por similitud de texto.\n", - "\n", - "# Contexto de la causa, párrafos con alguna mención a las personas:\n", - "{document_text}\n", - "\n", - "# Entidades Pre-clusterizadas a validar:\n", - "{canonical_entities}\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e07fffe7", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompts = [\n", - " system_prompt_1,\n", - " system_prompt_2,\n", - " system_prompt_3,\n", - " system_prompt_4,\n", - " system_prompt_5,\n", - " system_prompt_6,\n", - " system_prompt_7,\n", - "]\n", - "\n", - "user_prompt_templates = [\n", - " user_prompt_template_1,\n", - " user_prompt_template_2,\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "82835596", - "metadata": {}, - "outputs": [], - "source": [ - "def llm_infer_canonical_entities(\n", - " system_prompt: str,\n", - " user_prompt_template: str,\n", - " model: str,\n", - " document_path: Path,\n", - " pre_cluster_path: Path,\n", - " gold_path: Path,\n", - " context_window_length: int = 120,\n", - " model_context: int = 9_500,\n", - " target_label: str = \"PER\"\n", - ") -> dict:\n", - " \n", - " \"\"\"\n", - " Infers canonical entities using an LLM by providing context windows \n", - " around detected PER entities and pre-clusterization results.\n", - " \"\"\"\n", - "\n", - " # 1. Filename cleaning\n", - " clean_base_name = document_path.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", - " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", - "\n", - " print(f\"Processing document: {target_filename}\")\n", - "\n", - " # 2. Extract document text via API\n", - " session = requests.Session()\n", - " api_response = call_extraction_api(session, Path(document_path))\n", - " document_paragraphs = api_response.get(\"detail\", {}).get(\"document\")\n", - "\n", - " if not document_paragraphs:\n", - " raise ValueError(\"Document text is empty or not found for {target_filename}.\")\n", - "\n", - " # 3. Extract context windows for target label\n", - " context_windows = set()\n", - "\n", - " for paragraph in tqdm(document_paragraphs, desc=\"Extracting context\"):\n", - " preds = get_predictions(paragraph)\n", - " doc_text = preds['document']\n", - " \n", - " for label in preds['labels']:\n", - " label_type = label['attrs'].get('aymurai_label')\n", - " \n", - " # Filter: only PER labels\n", - " if label_type == target_label:\n", - " start = label['start_char']\n", - " end = label['end_char']\n", - " \n", - " # Define window boundaries\n", - " window_start = max(0, start - context_window_length)\n", - " window_end = min(len(doc_text), end + context_window_length)\n", - "\n", - " # Extract and clean the snippet\n", - " snippet = \" \".join(doc_text[window_start:window_end].split())\n", - " context_windows.add(snippet)\n", - "\n", - " context_windows = list(context_windows)\n", - " \n", - " # 4. Prepare Canonical Entities from Pre-cluster\n", - " canonical_entities_pre_cluster = load_json(json_file_path=pre_cluster_path)\n", - "\n", - " # Clean entities to save tokens\n", - " canonical_entities_prompt = [\n", - " {\n", - " k: v\n", - " for k, v in ce.items()\n", - " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", - " }\n", - " for ce in canonical_entities_pre_cluster\n", - " ]\n", - "\n", - " # 5. Build Final User Prompt\n", - " user_prompt = user_prompt_template.format(\n", - " document_text=\"\\n\".join(context_windows).strip(),\n", - " canonical_entities=get_pretty(canonical_entities_prompt),\n", - " )\n", - "\n", - " # 6. LLM Inference\n", - " provider = OllamaLLMProvider(model=model)\n", - " response = provider.generate(\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options={\"temperature\": 0, \"num_ctx\": model_context},\n", - " format=CanonicalEntities.model_json_schema(),\n", - " )\n", - "\n", - " # 7. Parse and Validate Output\n", - " raw_llm_output = [\n", - " output for output in json.loads(response.text)[\"canonical_entities\"]\n", - " ]\n", - "\n", - " canonical_entities_llm = [\n", - " CanonicalEntity.model_validate(canonical_entity)\n", - " for canonical_entity in raw_llm_output\n", - " ]\n", - "\n", - " # 8. Load Gold Standard for reference\n", - " canonical_entities_gold = load_json(json_file_path=gold_path)\n", - "\n", - " print(f\"\\nSummary for {target_filename}:\")\n", - " print(f\"- Pre-cluster entities: {len(canonical_entities_pre_cluster)}\")\n", - " print(f\"- LLM generated entities: {len(canonical_entities_llm)}\")\n", - " print(f\"- Gold standard entities: {len(canonical_entities_gold)}\")\n", - "\n", - " # Return structured results\n", - " return {\n", - " \"pre_cluster_json\": canonical_entities_pre_cluster,\n", - " \"llm_output_json\": canonical_entities_llm,\n", - " \"gold_standard_json\": canonical_entities_gold,\n", - " \"system_prompt\": system_prompt,\n", - " \"user_prompt\": user_prompt\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "586b1ddd", - "metadata": {}, - "outputs": [], - "source": [ - "def get_document_paths(document_check: Path, pre_clusterization_root: Path) -> dict:\n", - " \"\"\"\n", - " Generates the pre-cluster and gold JSON paths for a given document.\n", - " \n", - " Args:\n", - " document_check (Path): The original document Path object.\n", - " pre_clusterization_root (Path): The root directory for pre-clusterization data.\n", - " \n", - " Returns:\n", - " dict: A dictionary containing the 'pre_cluster_path' and 'gold_path'.\n", - " \"\"\"\n", - " # 1. Prepare target filename\n", - " clean_base_name = document_check.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", - " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", - "\n", - " # 2. Define subdirectories\n", - " pre_cluster_dir = pre_clusterization_root / 'best-pre-clusterization-per'\n", - " gold_dir = pre_clusterization_root / 'gold-jsons'\n", - "\n", - " # 3. Build final paths\n", - " pre_cluster_json_path = pre_cluster_dir / f\"{target_filename}-ner-preds-pre-cluster.json\"\n", - " gold_json_path = gold_dir / f\"{target_filename}-canonical-entities-gold.json\"\n", - "\n", - " return {\n", - " \"target_filename\": target_filename,\n", - " \"pre_cluster_path\": pre_cluster_json_path,\n", - " \"gold_path\": gold_json_path\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0c2163a5", - "metadata": {}, - "outputs": [], - "source": [ - "document_check = documents[0]\n", - "PRE_CLUSTERIZATION_ROOT = GOLD_JSON_ROOT.parent / \"pre-clusterization\"\n", - "paths = get_document_paths(document_check, PRE_CLUSTERIZATION_ROOT)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "42211950", - "metadata": {}, - "outputs": [], - "source": [ - "context_window_lengths = list(range(80, 200, 10))\n", - "\n", - "model_context_lengths = [7_500, 8_000, 8_500, 9_000, 9_500]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5f542f5", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict = llm_infer_canonical_entities(\n", - " system_prompt=system_prompts['system_prompt_7'],\n", - " user_prompt_template=user_prompt_templates['user_prompt_template_2'],\n", - " model=models[0],\n", - " document_path=document_check,\n", - " pre_cluster_path=paths[\"pre_cluster_path\"],\n", - " gold_path=paths[\"gold_path\"],\n", - " context_window_length=120,\n", - " model_context=9_500,\n", - " target_label=\"PER\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3a029e62", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict['llm_output_json']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "027b77cc", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict['pre_cluster_json']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1087e778", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict['gold_standard_json']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5c28436f", - "metadata": {}, - "outputs": [], - "source": [ - "print(llm_response_dict['user_prompt'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b51fc151", - "metadata": {}, - "outputs": [], - "source": [ - "print(llm_response_dict['system_prompt'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "90229978", - "metadata": {}, - "outputs": [], - "source": [ - "import shutil\n", - "\n", - "def get_name(obj):\n", - " \"\"\"Helper to extract a clean string name from a function or object.\"\"\"\n", - " if obj is None:\n", - " return \"None\"\n", - " if hasattr(obj, \"__name__\"):\n", - " return obj.__name__\n", - " # Fallback for complex objects or partials: remove memory addresses\n", - " clean_name = re.sub(r\" at 0x[0-9a-fA-F]+\", \"\", str(obj))\n", - " return clean_name.strip(\"<>\").replace(\"cyfunction \", \"\").replace(\"function \", \"\")\n", - "\n", - "\n", - "def run_llm_grid_search(\n", - " models: list,\n", - " system_prompts: dict,\n", - " user_prompt_templates: dict,\n", - " context_window_lengths: list,\n", - " model_context_lengths: list,\n", - " documents: list[Path],\n", - " target_label: str,\n", - " base_output_path: Path,\n", - "):\n", - " \"\"\"\n", - " Runs a grid search over hyperparameters using nested loops.\n", - " \"\"\"\n", - "\n", - " # To keep track of all results and find the best\n", - " all_combinations_results = []\n", - "\n", - " for model in models:\n", - " for system_prompt_name, system_prompt in system_prompts.items():\n", - " for user_prompt_template_name, user_prompt_template in user_prompt_templates.items():\n", - " for context_window_length in context_window_lengths:\n", - " for model_context in model_context_lengths:\n", - " \n", - " print(\n", - " f\"\\nRunning grid search with model: {model}, \"\n", - " f\"context_window_length: {context_window_length}, \"\n", - " f\"model_context: {model_context}\"\n", - " )\n", - "\n", - " # 1. Create a descriptive folder name with the cleaned names\n", - " combo_name = (\n", - " f\"model-{model}-system_prompt-{system_prompt_name}-\"\n", - " f\"user_prompt_template-{user_prompt_template_name}-context_window-{context_window_length}-\"\n", - " f\"model_context-{model_context}\"\n", - " )\n", - "\n", - " combo_dir = base_output_path / combo_name\n", - " combo_dir.mkdir(parents=True, exist_ok=True)\n", - "\n", - " metrics_results = {}\n", - " metrics_file_path = (\n", - " combo_dir / f\"evaluation_metrics_{target_label}.json\"\n", - " )\n", - "\n", - " # List to collect scores for this specific combination\n", - " current_combo_scores = []\n", - "\n", - " for document in documents:\n", - " \n", - " # Get document paths\n", - " paths = get_document_paths(document, PRE_CLUSTERIZATION_ROOT)\n", - "\n", - " # Run LLM inference\n", - " llm_response_dict = llm_infer_canonical_entities(\n", - " system_prompt=system_prompt,\n", - " user_prompt_template=user_prompt_template,\n", - " model=model,\n", - " document_path=document,\n", - " pre_cluster_path=paths['pre_cluster_path'],\n", - " gold_path=paths['gold_path'],\n", - " context_window_length=context_window_length,\n", - " model_context=model_context,\n", - " target_label=target_label\n", - " )\n", - " # Extract predictions\n", - " ce_llm = llm_response_dict['llm_output_json']\n", - " \n", - " # Validate and prepare for saving\n", - " ce_llm = [\n", - " CanonicalEntity.model_validate(entity)\n", - " for entity in ce_llm\n", - " ]\n", - "\n", - " ce_llm = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in ce_llm\n", - " ]\n", - "\n", - " # Save the result as the llm-pred.json\n", - " target_filename = paths['target_filename']\n", - " llm_filename = f\"{target_filename}-llm-pred.json\"\n", - " llm_output_path = combo_dir / llm_filename\n", - "\n", - " save_json(\n", - " file_path=llm_output_path, json_data=ce_llm\n", - " )\n", - "\n", - " # Extract gold standard\n", - " ce_gold = llm_response_dict['gold_standard_json']\n", - " \n", - " # Validate and prepare for saving\n", - " ce_gold = [\n", - " CanonicalEntity.model_validate(entity)\n", - " for entity in ce_gold\n", - " ]\n", - "\n", - " ce_gold = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in ce_gold\n", - " ]\n", - "\n", - " len_llm = len(ce_llm)\n", - " len_gold = len(ce_gold)\n", - "\n", - " # Evaluate metrics\n", - " score, metrics = aymurai_metrics.evaluate_disambiguation(\n", - " gold_json=ce_gold,\n", - " pred_json=ce_llm,\n", - " target_label=target_label,\n", - " )\n", - " current_combo_scores.append(score)\n", - " doc_id = target_filename\n", - " metrics_results[doc_id] = {\n", - " \"label\": target_label,\n", - " \"metric_value\": score,\n", - " \"detailed_metrics\": metrics,\n", - " \"len_llm_predictions\": len_llm,\n", - " \"len_gold_standard\": len_gold\n", - " }\n", - "\n", - " # --- Calculate Average for this combination ---\n", - " avg_score = (\n", - " np.mean(current_combo_scores) if current_combo_scores else 0.0\n", - " )\n", - "\n", - " # Add average to the results file for this folder\n", - " final_output = {\n", - " \"average_combination_score\": avg_score,\n", - " \"document_details\": metrics_results,\n", - " }\n", - "\n", - " with open(metrics_file_path, \"w\") as f:\n", - " json.dump(final_output, f, indent=4)\n", - "\n", - " # Store combination info for final ranking\n", - " all_combinations_results.append(\n", - " {\n", - " \"name\": combo_name,\n", - " \"score\": avg_score,\n", - " \"params\": {\n", - " \"model\": model,\n", - " \"system_prompt\": system_prompt_name,\n", - " \"user_prompt_template\": user_prompt_template_name,\n", - " \"context_window_length\": context_window_length,\n", - " \"model_context\": model_context\n", - " },\n", - " }\n", - " )\n", - "\n", - " # Save all combinations results in a summary file\n", - " summary_file_path = (\n", - " base_output_path / f\"results_summary_{target_label}.json\"\n", - " )\n", - " with open(summary_file_path, \"w\") as f:\n", - " json.dump(all_combinations_results, f, indent=4)\n", - "\n", - " # --- Find the best combination ---\n", - " best_combo = max(all_combinations_results, key=lambda x: x[\"score\"])\n", - "\n", - " print(f\"\\nGrid Search for {target_label} completed.\")\n", - "\n", - " # We now copy the best combination folder to a new location with a standardized name\n", - " src = Path(base_output_path) / best_combo[\"name\"]\n", - " # Combine the parent destination path with the new folder name\n", - " new_name = f\"best-llm-pred-{target_label.lower()}\"\n", - " dest = Path(base_output_path.parent) / new_name\n", - " \n", - " try:\n", - " # copytree creates the destination directory with the 'new_name'\n", - " shutil.copytree(src, dest)\n", - " print(f\"Successfully copied '{src.name}' to '{dest}'\")\n", - " except FileExistsError:\n", - " print(f\"Error: A folder named '{new_name}' already exists in '{base_output_path.parent}'\")\n", - " except Exception as e:\n", - " print(f\"An error occurred: {e}\") \n", - "\n", - " return best_combo" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5110dc60", - "metadata": {}, - "outputs": [], - "source": [ - "models = [\n", - " \"phi4:14b\",\n", - " \"gpt-oss:20b\",\n", - " \"llama3\",\n", - " \"llama3.1:8b\",\n", - " \"gemma3:270m\",\n", - "]\n", - "\n", - "system_prompts = {\n", - " \"system_prompt_1\": system_prompt_1,\n", - " \"system_prompt_2\": system_prompt_2,\n", - " \"system_prompt_3\": system_prompt_3,\n", - " \"system_prompt_4\": system_prompt_4,\n", - " \"system_prompt_5\": system_prompt_5,\n", - " \"system_prompt_6\": system_prompt_6,\n", - " \"system_prompt_7\": system_prompt_7\n", - "}\n", - "\n", - "\n", - "user_prompt_templates = {\n", - " \"user_prompt_template_1\": user_prompt_template_1,\n", - " \"user_prompt_template_2\": user_prompt_template_2,\n", - "}\n", - "\n", - "context_window_lengths = list(range(80, 200, 10))\n", - "\n", - "model_context_lengths = [7_500, 8_000, 8_500, 9_000, 9_500]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4fe27195", - "metadata": {}, - "outputs": [], - "source": [ - "models = [\n", - " \"phi4:14b\",\n", - "]\n", - "\n", - "system_prompts = {\n", - " \"system_prompt_7\": system_prompt_7\n", - "}\n", - "\n", - "\n", - "user_prompt_templates = {\n", - " \"user_prompt_template_2\": user_prompt_template_2,\n", - "}\n", - "\n", - "context_window_lengths = [120]\n", - "\n", - "model_context_lengths = [9_500]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a32b4244", - "metadata": {}, - "outputs": [], - "source": [ - "top_result = run_llm_grid_search(\n", - " models=models,\n", - " system_prompts=system_prompts,\n", - " user_prompt_templates=user_prompt_templates,\n", - " context_window_lengths=context_window_lengths,\n", - " model_context_lengths=model_context_lengths,\n", - " documents=documents[0:3],\n", - " target_label=\"PER\",\n", - " base_output_path=GOLD_JSON_ROOT.parent / \"llm-grid-search-results\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "86da6a45", - "metadata": {}, - "outputs": [], - "source": [ - "print(\n", - " f\"The best hyperparameter combination is '{top_result['name']}' \"\n", - " f\"with an average score of {top_result['score']:.4f}.\"\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai", - "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.10.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb index 61c88f82..ceaa9f84 100644 --- a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb +++ b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb @@ -13,6 +13,16 @@ "3) disambiguation\n" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "2edb7397", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich" + ] + }, { "cell_type": "code", "execution_count": null, @@ -26,7 +36,169 @@ "import os\n", "from pathlib import Path\n", "\n", - "import requests\n" + "import requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80f525f3", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template_PER = \"\"\"\n", + "Se proporciona la lista de entidades (personas) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su rol y validez.\n", + "\n", + "# Entidades Pre-clusterizadas a validar:\n", + "{canonical_entities}\n", + "\n", + "Procesa la lista siguiendo las instrucciones de filtrado, fusión y asignación de roles.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c8f2ce2", + "metadata": {}, + "outputs": [], + "source": [ + "user_prompt_template_DIR = \"\"\"\n", + "Se proporciona la lista de entidades (direcciones) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su validez y si hay otras entidades de direcciones que corresponden a esa.\n", + "\n", + "# Entidades Pre-clusterizadas a validar:\n", + "{canonical_entities}\n", + "\n", + "Procesa la lista siguiendo las instrucciones de filtrado y fusión.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1aa9140e", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_PER = \"\"\"\n", + "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Asignación de Rol:** Identifica el rol procesal basándote en las ventanas de contexto dentro de cada entidad canónica en la parte de `attributes`['context']\n", + " Elija un rol EXCLUSIVAMENTE en esta lista:\n", + " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", + "2. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", + "3. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", + " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", + " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", + "4. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", + "5. **Limpieza:** Limpiá los `aliases` y el `canonical_text` si los mismos tienen otras palabras, pero respeta que estén escritos de igual manera a que sus ventanas de contexto.\n", + " Te dejo un ejemplo de esta instrucción.\n", + " Vos recibís:\n", + " {\n", + " \"canonical_text\": \"por DREXLER, JORGE\",\n", + " \"aliases\": [\n", + " \"por DREXLER, JORGE\",\n", + " \"Jorge Drexler\"\n", + " ],\n", + " \"attributes\": {\n", + " \"context\": [\n", + " \"Fdo. por DREXLER, JORGE - JUEZ DE CÁMARA el mismo cita en el documento 56 que Juan es culpable\",\n", + " \"es acaso Jorge Drexler el juez designado para esta causa\"\n", + " ]\n", + " }\n", + "\n", + " Debés entregar:\n", + " {\n", + " \"canonical_text\": \"DREXLER, JORGE\",\n", + " \"aliases\": [\n", + " \"DREXLER, JORGE\",\n", + " \"Jorge Drexler\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Juez/a\"\n", + " }\n", + "\n", + "6. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", + "\n", + "### Formato de Salida (JSON):\n", + "Devuelve un array de objetos con esta estructura:\n", + "[\n", + " {\n", + " \"entity_id\": \"optional-unique-id\",\n", + " \"aymurai_label\": \"PER\",\n", + " \"canonical_text\": \"Nombre de la entidad\",\n", + " \"aliases\": [\n", + " \"Nombre de la entidad\",\n", + " \"Alias 1\",\n", + " \"Alias 2\"\n", + " ],\n", + " \"attributes\": {\n", + " \"role\": \"Rol de la lista o null\"\n", + " }\n", + " }\n", + "]\n", + "\n", + "No incluyas el campo 'context' en tu respuesta final.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "61fb70d5", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d67ad4f2", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt_DIR = \"\"\"\n", + "Eres un auditor experto en desambiguación de domicilios y direcciones en documentos judiciales. Tu tarea es validar y consolidar una lista de direcciones pre-agrupadas basándote en su contexto.\n", + "\n", + "### Instrucciones Estrictas:\n", + "1. **Consolidación Geográfica:** - Si dos entidades refieren a la misma ubicación física, fusiónalas en una sola entidad.\n", + " - Considera que una dirección puede estar escrita de forma completa en un alias (ej: \"Av. Rivadavia 123, CABA\") y de forma abreviada en otro (ej: \"Rivadavia 123\").\n", + " - Trata las variantes ortográficas o abreviaturas (Av. vs Avenida, Piso vs P., Dpto vs Departamento) como aliases de la misma ubicación.\n", + "2. **Filtrado:** Elimina entidades que no sean direcciones postales o domicilios físicos (ej: correos electrónicos, URLs, nombres de juzgados o menciones abstractas como \"el domicilio constituido\").\n", + "\n", + "### Formato de Salida (JSON):\n", + "[\n", + " {\n", + " \"entity_id\": \"ID original\",\n", + " \"aymurai_label\": \"DIRECCION\",\n", + " \"canonical_text\": \"Dirección más completa extraída del texto\",\n", + " \"aliases\": [\n", + " \"Dirección más completa (Igual al canonical)\",\n", + " \"Variante 1\",\n", + " \"Variante 2\"\n", + " ],\n", + " \"attributes\": []\n", + " }\n", + "]\n", + "**IMPORTANTE:** No incluyas el campo 'context' en la respuesta.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43dcd972", + "metadata": {}, + "outputs": [], + "source": [ + "SYSTEM_PROMPTS = {\n", + " \"PER\": system_prompt_PER,\n", + " \"DIRECCION\": system_prompt_DIR,\n", + "}\n", + "\n", + "USER_PROMPT_TEMPLATES = {\n", + " \"PER\": user_prompt_template_PER,\n", + " \"DIRECCION\": user_prompt_template_DIR,\n", + "}" ] }, { @@ -36,35 +208,27 @@ "metadata": {}, "outputs": [], "source": [ - "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8999\")\n", + "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8000\")\n", "DATA_ROOT = Path(\n", " os.getenv(\n", " \"DISAMBIGUATION_DATA_ROOT\",\n", - " \"/resources/data/restricted/disambiguation-eval/documents\",\n", + " \"../../../resources/data/restricted/disambiguation-eval/files\",\n", " )\n", ")\n", "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", "\n", "LIMIT = int(os.getenv(\"DISAMBIGUATION_DOC_LIMIT\", \"0\")) # 0 = no limit\n", - "TARGET_LABELS = os.getenv(\"DISAMBIGUATION_TARGET_LABELS\", \"PER\")\n", - "TARGET_LABELS = [label.strip() for label in TARGET_LABELS.split(\",\") if label.strip()]\n", - "\n", - "FUZZY_THRESHOLD = int(os.getenv(\"DISAMBIGUATION_THRESHOLD\", \"70\"))\n", - "FUZZY_SCORER = os.getenv(\"DISAMBIGUATION_SCORER\", \"token_set_ratio\")\n", - "FUZZY_PROCESSOR = os.getenv(\"DISAMBIGUATION_PROCESSOR\", \"light_normalizer\")\n", + "TARGET_LABELS = os.getenv(\"DISAMBIGUATION_TARGET_LABELS\")\n", "\n", "print(f\"API: {API_BASE_URL}\")\n", "print(f\"Data root: {DATA_ROOT}\")\n", - "print(f\"Target labels: {TARGET_LABELS or 'ALL'}\")\n", - "print(\n", - " f\"Fuzzy params: scorer={FUZZY_SCORER}, threshold={FUZZY_THRESHOLD}, processor={FUZZY_PROCESSOR}\"\n", - ")\n" + "print(f\"Target labels: {TARGET_LABELS or 'All the targets that have the prompt set'}\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "4d3d5787", + "id": "58e9d69e", "metadata": {}, "outputs": [], "source": [ @@ -86,25 +250,55 @@ " endpoint = f\"{API_BASE_URL}/anonymizer/predict\"\n", " predictions = []\n", " for paragraph in paragraphs:\n", - " response = requests.post(endpoint, json={\"text\": paragraph}, timeout=60)\n", + " response = requests.post(\n", + " endpoint, json={\"text\": paragraph}, params={\"use_cache\": False}, timeout=60\n", + " )\n", " response.raise_for_status()\n", " predictions.append(response.json())\n", " return predictions\n", "\n", "\n", - "def disambiguate(predictions: list[dict]) -> dict:\n", + "def disambiguate(\n", + " predictions: list[dict], use_custom_prompts: bool = True\n", + ") -> list[dict]:\n", " endpoint = f\"{API_BASE_URL}/anonymizer/disambiguate\"\n", - " params = {\n", - " \"threshold\": FUZZY_THRESHOLD,\n", - " \"scorer\": FUZZY_SCORER,\n", - " \"processor\": FUZZY_PROCESSOR,\n", - " \"target_labels\": TARGET_LABELS,\n", - " }\n", + "\n", + " payload = {\"paragraphs\": predictions}\n", + "\n", + " if use_custom_prompts:\n", + " custom_prompts_list = []\n", + "\n", + " for label in SYSTEM_PROMPTS.keys():\n", + " custom_prompts_list.append(\n", + " {\n", + " \"label\": label,\n", + " \"system\": SYSTEM_PROMPTS.get(label, \"\"),\n", + " \"user\": USER_PROMPT_TEMPLATES.get(label, \"\"),\n", + " }\n", + " )\n", + "\n", + " payload[\"custom_prompts\"] = custom_prompts_list\n", + "\n", + " else:\n", + " payload[\"custom_prompts\"] = []\n", + "\n", + " params = {\"use_cache\": False}\n", + "\n", " if TARGET_LABELS:\n", " params[\"target_labels\"] = TARGET_LABELS\n", - " response = requests.post(endpoint, params=params, json=predictions, timeout=120)\n", + "\n", + " else:\n", + " params[\"target_labels\"] = []\n", + "\n", + " response = requests.post(\n", + " endpoint,\n", + " params=params,\n", + " json=payload,\n", + " timeout=600,\n", + " )\n", + "\n", " response.raise_for_status()\n", - " return response.json()\n" + " return response.json()" ] }, { @@ -118,21 +312,17 @@ "if LIMIT > 0:\n", " documents = documents[:LIMIT]\n", "\n", - "print(f\"Found {len(documents)} documents\")\n", - "documents[:5]\n" + "print(f\"Found {len(documents)} documents\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "1eb1c141", + "id": "d844f2c8", "metadata": {}, "outputs": [], "source": [ - "from aymurai.experiments.entity_disambiguation.runner import call_extraction_api\n", - "\n", - "\n", - "for doc_path in documents:\n", + "for doc_path in documents[2:3]:\n", " print(f\"\\n=== {doc_path.name} ===\")\n", " session = requests.Session()\n", " document = extract_document(\n", @@ -147,15 +337,55 @@ " predictions = predict_paragraphs(paragraphs)\n", " print(f\"Predictions: {len(predictions)}\")\n", "\n", - " disambiguated = disambiguate(predictions)\n", - " print(f\"Canonical entities: {len(disambiguated.get('canonical_entities', []))}\")\n", - " print(json.dumps(disambiguated, indent=2, ensure_ascii=False))\n" + " disambiguated = disambiguate(predictions=predictions, use_custom_prompts=False)\n", + " print(f\"NER prdictions: {len(disambiguated.get('data', []))}\")\n", + " print(json.dumps(disambiguated, indent=2, ensure_ascii=False))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f65c14ab", + "metadata": {}, + "outputs": [], + "source": [ + "predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0eb0561f", + "metadata": {}, + "outputs": [], + "source": [ + "disambiguated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7296e8a", + "metadata": {}, + "outputs": [], + "source": [ + "for pred in disambiguated.get(\"data\", []):\n", + " labels = pred.get(\"labels\", [])\n", + " if labels:\n", + " for label in labels:\n", + " attrs = label.get(\"attrs\", {})\n", + " if attrs.get(\"aymurai_label\") == \"FECHA\":\n", + " print(\n", + " label.get(\"text\"),\n", + " attrs.get(\"aymurai_label_subclass\"),\n", + " attrs.get(\"canonical_entity_id\"),\n", + " )" ] }, { "cell_type": "code", "execution_count": null, - "id": "877f5167", + "id": "0f95a791", "metadata": {}, "outputs": [], "source": [] diff --git a/notebooks/experiments/entity-disambiguation/09-date-formatter-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/09-date-formatter-disambiguation.ipynb new file mode 100644 index 00000000..0cefa239 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/09-date-formatter-disambiguation.ipynb @@ -0,0 +1,360 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "185b896a", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c675263", + "metadata": {}, + "outputs": [], + "source": [ + "# import locale\n", + "# import platform\n", + "\n", + "# locales_a_probar = ['es_AR.UTF-8', 'es_ES.UTF-8', 'es_MX.UTF-8', 'spanish']\n", + "\n", + "# for loc in locales_a_probar:\n", + "# try:\n", + "# locale.setlocale(locale.LC_ALL, loc)\n", + "# break\n", + "# except locale.Error:\n", + "# continue\n", + "\n", + "# print(f\"Sistema: {platform.system()}\")\n", + "# print(f\"Locale actual: {locale.getlocale()}\")\n", + "# print(f\"Default Locale: {locale.getdefaultlocale()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6640fde2", + "metadata": {}, + "outputs": [], + "source": [ + "# # 1. Forzamos a Python a que crea que \"es_AR.UTF-8\" es lo mismo que el locale básico\n", + "# # Esto intercepta la llamada que falla en patterns.py:4\n", + "# def mock_setlocale(category, value=None):\n", + "# return \"C\" # 'C' es el locale universal que siempre existe\n", + "\n", + "# locale.setlocale = mock_setlocale\n", + "\n", + "# # 2. Opcional: Si quieres ser más preciso, intenta usar el español genérico\n", + "# try:\n", + "# # En Mac a veces es 'es_ES.UTF-8' o solo 'es_AR' (sin .UTF-8)\n", + "# # Aquí intentamos forzar uno que funcione\n", + "# import _locale\n", + "# _locale._setlocale(locale.LC_ALL, 'en_US.UTF-8') \n", + "# except:\n", + "# pass\n", + "\n", + "# print(\"✅ Hack de locale aplicado. Ya puedes importar aymurai.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "692b5bfe", + "metadata": {}, + "outputs": [], + "source": [ + "from aymurai.transforms.datetime_formatter import DatetimeFormatter\n", + "from aymurai.meta.api_interfaces import DocLabel\n", + "from aymurai.utils.entity_disambiguation.date_formatter import get_canonical_dates" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54ad952d", + "metadata": {}, + "outputs": [], + "source": [ + "formatter = DatetimeFormatter()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73946d4a", + "metadata": {}, + "outputs": [], + "source": [ + "ent = DocLabel(\n", + " text= \"el dia 23 de enero de 2024\",\n", + " start_char= 0,\n", + " end_char= 500,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"enero de 2024\",\n", + " \"aymurai_alt_start_char\": 0,\n", + " \"aymurai_alt_end_char\": 400,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.7974909742673238,\n", + " \"aymurai_label_instance\": 2,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": True,\n", + " \"canonical_entity_id\": \"e8a5378d-bbc1-50f0-8da1-27871d5a80a2\"\n", + " }\n", + ")\n", + "\n", + "date_1 = DocLabel(\n", + " text=\"Buenos Aires, 14 de mayo de 2023\",\n", + " start_char=10,\n", + " end_char=42,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"14/05/2023\",\n", + " \"aymurai_alt_start_char\": 24,\n", + " \"aymurai_alt_end_char\": 42,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.9543,\n", + " \"aymurai_label_instance\": 1,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": \"fe54c1a2-b3d4-4e5f-a6b7-c8d9e0f1a2b3\"\n", + " }\n", + ")\n", + "\n", + "date_2 = DocLabel(\n", + " text=\"audiencia del 03 de febrero de 2024\",\n", + " start_char=150,\n", + " end_char=185,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"03/02/2024\",\n", + " \"aymurai_alt_start_char\": 164,\n", + " \"aymurai_alt_end_char\": 185,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.8876,\n", + " \"aymurai_label_instance\": 2,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": \"a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d\"\n", + " }\n", + ")\n", + "\n", + "date_3 = DocLabel(\n", + " text=\"22/10/2023\",\n", + " start_char=402,\n", + " end_char=412,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"22/10/2023\",\n", + " \"aymurai_alt_start_char\": 402,\n", + " \"aymurai_alt_end_char\": 412,\n", + " \"aymurai_method\": \"regex/patterns\",\n", + " \"aymurai_score\": 1.0,\n", + " \"aymurai_label_instance\": 3,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": \"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e\"\n", + " }\n", + ")\n", + "\n", + "date_4 = DocLabel(\n", + " text=\"notificado el pasado lunes 11\",\n", + " start_char=820,\n", + " end_char=849,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"11\",\n", + " \"aymurai_alt_start_char\": 847,\n", + " \"aymurai_alt_end_char\": 849,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.6124,\n", + " \"aymurai_label_instance\": 4,\n", + " \"aymurai_disambiguation\": \"llm\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": \"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f\"\n", + " }\n", + ")\n", + "\n", + "date_5 = DocLabel(\n", + " text=\"primero de enero de dos mil veintiuno\",\n", + " start_char=1100,\n", + " end_char=1137,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"01/01/2021\",\n", + " \"aymurai_alt_start_char\": 1100,\n", + " \"aymurai_alt_end_char\": 1137,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.7942,\n", + " \"aymurai_label_instance\": 5,\n", + " \"aymurai_disambiguation\": \"llm\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": \"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a\"\n", + " }\n", + ")\n", + "\n", + "\n", + "labels_baseline = [ent, date_1, date_2, date_3, date_4, date_5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "417e0719", + "metadata": {}, + "outputs": [], + "source": [ + "date_1 = DocLabel(\n", + " text=\"el dia 23 de enero de 2024\",\n", + " start_char=0,\n", + " end_char=26,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"23 de enero de 2024\",\n", + " \"aymurai_alt_start_char\": 7,\n", + " \"aymurai_alt_end_char\": 26,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.98,\n", + " \"aymurai_label_instance\": 1,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": None\n", + " }\n", + ")\n", + "\n", + "date_2 = DocLabel(\n", + " text=\"hechos del 23/01/24\",\n", + " start_char=150,\n", + " end_char=169,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"23/01/24\",\n", + " \"aymurai_alt_start_char\": 11,\n", + " \"aymurai_alt_end_char\": 19,\n", + " \"aymurai_method\": \"regex/patterns\",\n", + " \"aymurai_score\": 1.0,\n", + " \"aymurai_label_instance\": 2,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": None\n", + " }\n", + ")\n", + "\n", + "date_3 = DocLabel(\n", + " text=\"ocurrido el 23 enero 2024\",\n", + " start_char=300,\n", + " end_char=325,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"23 enero 2024\",\n", + " \"aymurai_alt_start_char\": 12,\n", + " \"aymurai_alt_end_char\": 25,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.91,\n", + " \"aymurai_label_instance\": 3,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": None\n", + " }\n", + ")\n", + "\n", + "date_4 = DocLabel(\n", + " text=\"23-01-2024\",\n", + " start_char=450,\n", + " end_char=460,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"23-01-2024\",\n", + " \"aymurai_alt_start_char\": 450,\n", + " \"aymurai_alt_end_char\": 460,\n", + " \"aymurai_method\": \"regex/patterns\",\n", + " \"aymurai_score\": 1.0,\n", + " \"aymurai_label_instance\": 4,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": None\n", + " }\n", + ")\n", + "\n", + "date_5 = DocLabel(\n", + " text=\"23 de enero\",\n", + " start_char=600,\n", + " end_char=618,\n", + " attrs={\n", + " \"aymurai_label\": \"FECHA\",\n", + " \"aymurai_label_subclass\": [],\n", + " \"aymurai_alt_text\": \"23 de enero\",\n", + " \"aymurai_alt_start_char\": 600,\n", + " \"aymurai_alt_end_char\": 618,\n", + " \"aymurai_method\": \"ner/flair\",\n", + " \"aymurai_score\": 0.99,\n", + " \"aymurai_label_instance\": 5,\n", + " \"aymurai_disambiguation\": \"fuzzy\",\n", + " \"aymurai_anonymize\": False,\n", + " \"canonical_entity_id\": None\n", + " }\n", + ")\n", + "\n", + "batch_dates = [date_1, date_2, date_3, date_4, date_5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b9581fd", + "metadata": {}, + "outputs": [], + "source": [ + "ce = get_canonical_dates(batch_dates)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7b7a58a", + "metadata": {}, + "outputs": [], + "source": [ + "ce" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/resources/llm/entity_disambiguation.yml b/resources/llm/entity_disambiguation.yml new file mode 100644 index 00000000..14615b37 --- /dev/null +++ b/resources/llm/entity_disambiguation.yml @@ -0,0 +1,69 @@ +PER: + system: | + Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas. + + ### Instrucciones Estrictas: + 1. **Asignación de Rol:** Identifica el rol procesal basándote en las ventanas de contexto dentro de cada entidad canónica en la parte de `attributes`['context'] + Elija un rol EXCLUSIVAMENTE en esta lista: + - "Denunciante", "Denunciado/a", "Víctima", "Juez/a", "Defensor/a de Cámara", "Asesor/a Tutelar", "Fiscal", "Abogado/a", "Perito/a", "Testigo". + 2. **Regla "Dr/a":** Si se menciona como "Dr.", "Dra." o "Dres." y no hay otro cargo explícito, asígnale el rol "Abogado/a". + 3. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona. + - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos. + - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo. + 4. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes). + 5. **Limpieza:** Limpiá los `aliases` y el `canonical_text` si los mismos tienen otras palabras, pero respeta que estén escritos de igual manera a que sus ventanas de contexto. + Te dejo un ejemplo de esta instrucción. + Vos recibís: + { + "canonical_text": "por DREXLER, JORGE", + "aliases": [ + "por DREXLER, JORGE", + "Jorge Drexler" + ], + "attributes": { + "context": [ + "Fdo. por DREXLER, JORGE - JUEZ DE CÁMARA el mismo cita en el documento 56 que Juan es culpable", + "es acaso Jorge Drexler el juez designado para esta causa" + ] + } + + Debés entregar: + { + "canonical_text": "DREXLER, JORGE", + "aliases": [ + "DREXLER, JORGE", + "Jorge Drexler" + ], + "attributes": { + "role": "Juez/a" + } + + 6. **Manejo de Iniciales:** Identifica si las siglas (ej: "M.L.") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma. + + ### Formato de Salida (JSON): + Devuelve un array de objetos con esta estructura: + [ + { + "entity_id": "optional-unique-id", + "aymurai_label": "PER", + "canonical_text": "Nombre de la entidad", + "aliases": [ + "Nombre de la entidad", + "Alias 1", + "Alias 2" + ], + "attributes": { + "role": "Rol de la lista o null" + } + } + ] + + No incluyas el campo 'context' en tu respuesta final. + + user: | + Se proporciona la lista de entidades (personas) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su rol y validez. + + # Entidades Pre-clusterizadas a validar: + {canonical_entities} + + Procesa la lista siguiendo las instrucciones de filtrado, fusión y asignación de roles. diff --git a/resources/pipelines/production/flair-anonymizer/pipeline.json b/resources/pipelines/production/flair-anonymizer/pipeline.json index 07e4638e..75302833 100644 --- a/resources/pipelines/production/flair-anonymizer/pipeline.json +++ b/resources/pipelines/production/flair-anonymizer/pipeline.json @@ -20,6 +20,10 @@ [ "aymurai.transforms.anonymization_postprocess.core.AnonymizationEntityCleaner", {} + ], + [ + "aymurai.transforms.datetime_formatter.core.DatetimeFormatter", + {} ] ], "use_cache": false diff --git a/uv.lock b/uv.lock index 9913d119..a903aa6d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,19 +1,22 @@ version = 1 -revision = 3 requires-python = "==3.10.*" resolution-markers = [ - "sys_platform == 'darwin'", - "platform_machine == 'aarch64' and sys_platform == 'linux'", - "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", + "platform_system == 'Darwin' and sys_platform == 'darwin'", + "platform_system != 'Darwin' and sys_platform == 'darwin'", + "platform_machine == 'aarch64' and platform_system == 'Linux' and sys_platform == 'linux'", + "platform_machine == 'aarch64' and platform_system != 'Linux' and sys_platform == 'linux'", + "(platform_machine != 'aarch64' and platform_system == 'Darwin' and sys_platform == 'linux') or (platform_system == 'Darwin' and sys_platform != 'darwin' and sys_platform != 'linux')", + "platform_machine == 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux'", + "(platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')", ] [[package]] name = "absl-py" version = "2.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" } +sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" }, + { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811 }, ] [[package]] @@ -38,9 +41,9 @@ wheels = [ name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, ] [[package]] @@ -86,9 +89,9 @@ dependencies = [ { name = "frozenlist" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, ] [[package]] @@ -101,9 +104,9 @@ dependencies = [ { name = "tomli" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064, upload-time = "2025-11-14T20:35:04.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554, upload-time = "2025-11-14T20:35:05.699Z" }, + { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554 }, ] [[package]] @@ -126,18 +129,18 @@ wheels = [ name = "annotated-doc" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] [[package]] @@ -153,9 +156,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191, upload-time = "2025-02-18T20:35:33.314Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228, upload-time = "2025-02-18T20:35:28.659Z" }, + { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228 }, ] [[package]] @@ -176,9 +179,9 @@ wheels = [ name = "appnope" version = "0.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, ] [[package]] @@ -188,9 +191,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, ] [[package]] @@ -200,23 +203,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, - { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549, upload-time = "2025-07-30T10:02:00.101Z" }, - { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539, upload-time = "2025-07-30T10:02:00.929Z" }, - { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467, upload-time = "2025-07-30T10:02:02.08Z" }, - { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355, upload-time = "2025-07-30T10:02:02.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187, upload-time = "2025-07-30T10:02:03.674Z" }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, + { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549 }, + { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539 }, + { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467 }, + { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355 }, + { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187 }, ] [[package]] @@ -227,18 +230,18 @@ dependencies = [ { name = "python-dateutil" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, ] [[package]] name = "asttokens" version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, ] [[package]] @@ -248,18 +251,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380, upload-time = "2025-03-16T17:25:36.919Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380 } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069, upload-time = "2025-03-16T17:25:35.422Z" }, + { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069 }, ] [[package]] name = "async-timeout" version = "5.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, ] [[package]] @@ -269,23 +272,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217, upload-time = "2024-08-24T23:15:36.449Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209, upload-time = "2024-08-24T23:15:35.317Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209 }, ] [[package]] name = "attrs" version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, ] [[package]] name = "aymurai" -version = "2.0.0a6.dev5" +version = "2.0.0a7.dev37" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -412,9 +415,9 @@ mlops = [ name = "babel" version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] [[package]] @@ -425,9 +428,9 @@ dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, ] [[package]] @@ -441,9 +444,9 @@ dependencies = [ { name = "lxml" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924, upload-time = "2023-08-15T22:34:59.047Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419, upload-time = "2023-08-15T22:34:56.022Z" }, + { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419 }, ] [[package]] @@ -453,9 +456,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533, upload-time = "2025-10-27T17:57:39.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437, upload-time = "2025-10-27T17:57:37.538Z" }, + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, ] [package.optional-dependencies] @@ -467,9 +470,9 @@ css = [ name = "blinker" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, ] [[package]] @@ -525,54 +528,54 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283 }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504 }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811 }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402 }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217 }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079 }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475 }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829 }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211 }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184 }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790 }, ] [[package]] name = "cfgv" version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, ] [[package]] name = "charset-normalizer" version = "3.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, - { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, - { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, - { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, - { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, - { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, - { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, - { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, - { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, - { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, - { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, ] [[package]] @@ -580,29 +583,29 @@ name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Windows' and sys_platform == 'linux') or (platform_system == 'Windows' and sys_platform != 'darwin' and sys_platform != 'linux')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, ] [[package]] name = "cloudpickle" version = "3.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] @@ -610,29 +613,29 @@ name = "colorlog" version = "6.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743 }, ] [[package]] name = "comm" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, ] [[package]] name = "conllu" version = "4.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768, upload-time = "2023-06-19T12:37:49.632Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098, upload-time = "2023-06-19T12:37:47.885Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098 }, ] [[package]] @@ -642,7 +645,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } wheels = [ { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551 }, { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399 }, @@ -707,7 +710,7 @@ wheels = [ name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, ] @@ -773,18 +776,18 @@ wheels = [ name = "decorator" version = "5.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, ] [[package]] name = "defusedxml" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, ] [[package]] @@ -794,52 +797,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, ] [[package]] name = "dill" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668 }, ] [[package]] name = "diskcache" version = "5.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, ] [[package]] name = "distro" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, ] [[package]] name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, ] @@ -862,15 +865,15 @@ wheels = [ name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } [[package]] name = "docx2txt" version = "0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613, upload-time = "2025-03-24T20:59:25.21Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025, upload-time = "2025-03-24T20:59:24.394Z" }, + { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025 }, ] [[package]] @@ -906,9 +909,9 @@ wheels = [ name = "einops" version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805, upload-time = "2025-02-09T03:17:00.434Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359, upload-time = "2025-02-09T03:17:01.998Z" }, + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, ] [[package]] @@ -919,9 +922,9 @@ dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, ] [[package]] @@ -931,18 +934,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 }, ] [[package]] name = "executing" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, ] [[package]] @@ -952,9 +955,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644, upload-time = "2023-06-27T15:24:28.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644 } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039, upload-time = "2023-06-27T15:24:24.869Z" }, + { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039 }, ] [[package]] @@ -1028,62 +1031,62 @@ wheels = [ name = "fastar" version = "0.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536, upload-time = "2025-11-26T02:34:35.236Z" }, - { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235, upload-time = "2025-11-26T02:34:19.367Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386, upload-time = "2025-11-26T02:33:47.613Z" }, - { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955, upload-time = "2025-11-26T02:32:44.279Z" }, - { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987, upload-time = "2025-11-26T02:32:59.701Z" }, - { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900, upload-time = "2025-11-26T02:33:16.059Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523, upload-time = "2025-11-26T02:33:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268, upload-time = "2025-11-26T02:34:04.003Z" }, - { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286, upload-time = "2025-11-26T02:34:50.279Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921, upload-time = "2025-11-26T02:35:07.292Z" }, - { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302, upload-time = "2025-11-26T02:35:24.995Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141, upload-time = "2025-11-26T02:35:42.449Z" }, - { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544, upload-time = "2025-11-26T02:36:23.801Z" }, - { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701, upload-time = "2025-11-26T02:36:09.625Z" }, - { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544, upload-time = "2025-11-26T02:34:46.195Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020, upload-time = "2025-11-26T02:34:31.085Z" }, - { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735, upload-time = "2025-11-26T02:34:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779, upload-time = "2025-11-26T02:32:55.109Z" }, - { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755, upload-time = "2025-11-26T02:33:11.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732, upload-time = "2025-11-26T02:33:27.122Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571, upload-time = "2025-11-26T02:33:42.986Z" }, - { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440, upload-time = "2025-11-26T02:34:15.439Z" }, - { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424, upload-time = "2025-11-26T02:35:02.742Z" }, - { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675, upload-time = "2025-11-26T02:35:20.252Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098, upload-time = "2025-11-26T02:35:37.643Z" }, - { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397, upload-time = "2025-11-26T02:35:56.215Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536 }, + { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235 }, + { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386 }, + { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955 }, + { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987 }, + { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900 }, + { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523 }, + { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268 }, + { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286 }, + { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921 }, + { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302 }, + { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141 }, + { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544 }, + { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701 }, + { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544 }, + { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020 }, + { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735 }, + { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779 }, + { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755 }, + { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732 }, + { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571 }, + { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440 }, + { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424 }, + { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675 }, + { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098 }, + { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397 }, ] [[package]] name = "fastjsonschema" version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, ] [[package]] name = "fastuuid" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760, upload-time = "2025-10-19T22:25:21.509Z" }, - { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748, upload-time = "2025-10-19T22:41:52.873Z" }, - { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537, upload-time = "2025-10-19T22:33:55.603Z" }, - { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994, upload-time = "2025-10-19T22:26:17.631Z" }, - { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003, upload-time = "2025-10-19T22:23:45.415Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583, upload-time = "2025-10-19T22:26:00.756Z" }, - { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955, upload-time = "2025-10-19T22:36:25.196Z" }, - { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763, upload-time = "2025-10-19T22:24:28.451Z" }, - { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613, upload-time = "2025-10-19T22:25:06.827Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045, upload-time = "2025-10-19T22:28:32.732Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122, upload-time = "2025-10-19T22:23:15.59Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760 }, + { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748 }, + { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537 }, + { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994 }, + { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003 }, + { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583 }, + { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955 }, + { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763 }, + { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613 }, + { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045 }, + { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122 }, ] [[package]] @@ -1099,9 +1102,9 @@ wheels = [ name = "filetype" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020 } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970 }, ] [[package]] @@ -1111,9 +1114,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "termcolor" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720, upload-time = "2025-08-16T20:20:24.175Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945, upload-time = "2025-08-16T20:20:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945 }, ] [[package]] @@ -1147,7 +1150,7 @@ dependencies = [ { name = "transformers", extra = ["sentencepiece"] }, { name = "wikipedia-api" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607, upload-time = "2025-02-05T14:45:44.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607 } wheels = [ { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, ] @@ -1203,43 +1206,43 @@ wheels = [ name = "fqdn" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, ] [[package]] name = "frozenlist" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, - { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, - { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, - { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, - { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, - { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, - { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, - { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, - { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, - { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, - { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, - { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230 }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621 }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889 }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464 }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649 }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188 }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748 }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351 }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767 }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887 }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785 }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312 }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650 }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659 }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837 }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, ] [[package]] name = "fsspec" version = "2025.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966 }, ] [package.optional-dependencies] @@ -1254,9 +1257,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, ] [[package]] @@ -1290,9 +1293,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smmap" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, ] [[package]] @@ -1318,9 +1321,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759, upload-time = "2025-10-28T21:34:51.529Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706, upload-time = "2025-10-28T21:34:50.151Z" }, + { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706 }, ] [[package]] @@ -1349,9 +1352,9 @@ dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027, upload-time = "2025-10-29T23:17:39.513Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027 } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469, upload-time = "2025-10-29T23:17:38.548Z" }, + { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469 }, ] [[package]] @@ -1412,9 +1415,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-crc32c" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265, upload-time = "2025-11-17T15:38:06.659Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340, upload-time = "2025-11-17T15:38:05.594Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340 }, ] [[package]] @@ -1469,7 +1472,7 @@ wheels = [ name = "greenlet" version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651 } wheels = [ { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658 }, { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810 }, @@ -1497,24 +1500,24 @@ wheels = [ name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] [[package]] name = "hf-xet" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020 } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099 }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178 }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214 }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054 }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812 }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920 }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 }, ] [[package]] @@ -1525,24 +1528,24 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, ] [[package]] name = "httptools" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" }, - { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" }, - { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" }, - { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" }, - { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531 }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408 }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889 }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460 }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267 }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429 }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173 }, ] [[package]] @@ -1555,7 +1558,7 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] @@ -1583,27 +1586,27 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358, upload-time = "2025-10-23T12:12:01.413Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094, upload-time = "2025-10-23T12:11:59.557Z" }, + { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094 }, ] [[package]] name = "identify" version = "2.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183 }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, ] [[package]] @@ -1635,7 +1638,7 @@ name = "ipykernel" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "appnope", marker = "(platform_machine != 'aarch64' and platform_system == 'Darwin') or (platform_system == 'Darwin' and sys_platform != 'linux')" }, { name = "comm" }, { name = "debugpy" }, { name = "ipython" }, @@ -1649,9 +1652,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579, upload-time = "2025-10-27T09:46:39.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968, upload-time = "2025-10-27T09:46:37.805Z" }, + { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968 }, ] [[package]] @@ -1659,7 +1662,7 @@ name = "ipython" version = "8.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, { name = "decorator" }, { name = "exceptiongroup" }, { name = "jedi" }, @@ -1687,9 +1690,9 @@ dependencies = [ { name = "traitlets" }, { name = "widgetsnbextension" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, ] [[package]] @@ -1699,7 +1702,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "arrow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } wheels = [ { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, ] @@ -1720,9 +1723,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parso" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, ] [[package]] @@ -1732,29 +1735,37 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] name = "jiter" version = "0.12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652, upload-time = "2025-11-09T20:46:41.021Z" }, - { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829, upload-time = "2025-11-09T20:46:43.281Z" }, - { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568, upload-time = "2025-11-09T20:46:45.075Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052, upload-time = "2025-11-09T20:46:46.818Z" }, - { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585, upload-time = "2025-11-09T20:46:48.319Z" }, - { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541, upload-time = "2025-11-09T20:46:49.643Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423, upload-time = "2025-11-09T20:46:51.731Z" }, - { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958, upload-time = "2025-11-09T20:46:53.432Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084, upload-time = "2025-11-09T20:46:54.848Z" }, - { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054, upload-time = "2025-11-09T20:46:56.487Z" }, - { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368, upload-time = "2025-11-09T20:46:58.638Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847, upload-time = "2025-11-09T20:47:00.295Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652 }, + { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829 }, + { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568 }, + { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052 }, + { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585 }, + { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541 }, + { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423 }, + { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958 }, + { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084 }, + { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054 }, + { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368 }, + { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847 }, + { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144 }, + { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877 }, + { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419 }, + { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212 }, + { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974 }, + { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233 }, + { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537 }, + { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110 }, ] [[package]] @@ -1765,18 +1776,18 @@ dependencies = [ { name = "click" }, { name = "rapidfuzz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537, upload-time = "2024-11-01T16:18:57.337Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990, upload-time = "2024-11-01T16:18:55.928Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990 }, ] [[package]] name = "jmespath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, + { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 }, ] [[package]] @@ -1813,18 +1824,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359, upload-time = "2023-09-01T12:34:44.187Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701, upload-time = "2023-09-01T12:34:42.563Z" }, + { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701 }, ] [[package]] name = "jsonpointer" version = "3.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, ] [[package]] @@ -1862,9 +1873,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, ] [[package]] @@ -1879,9 +1890,9 @@ dependencies = [ { name = "nbconvert" }, { name = "notebook" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, ] [[package]] @@ -1914,9 +1925,9 @@ dependencies = [ { name = "pyzmq" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, ] [[package]] @@ -1927,9 +1938,9 @@ dependencies = [ { name = "platformdirs" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, ] [[package]] @@ -1946,9 +1957,9 @@ dependencies = [ { name = "rfc3986-validator" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, + { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430 }, ] [[package]] @@ -1958,9 +1969,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823, upload-time = "2025-08-27T17:47:34.671Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687, upload-time = "2025-08-27T17:47:33.15Z" }, + { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687 }, ] [[package]] @@ -1980,7 +1991,7 @@ dependencies = [ { name = "overrides" }, { name = "packaging" }, { name = "prometheus-client" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "pyzmq" }, { name = "send2trash" }, { name = "terminado" }, @@ -1988,9 +1999,9 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949, upload-time = "2025-08-21T14:42:54.042Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221, upload-time = "2025-08-21T14:42:52.034Z" }, + { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221 }, ] [[package]] @@ -1998,12 +2009,12 @@ name = "jupyter-server-terminals" version = "0.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "terminado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430, upload-time = "2024-03-12T14:37:03.049Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656, upload-time = "2024-03-12T14:37:00.708Z" }, + { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656 }, ] [[package]] @@ -2035,9 +2046,9 @@ wheels = [ name = "jupyterlab-pygments" version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, ] [[package]] @@ -2053,44 +2064,44 @@ dependencies = [ { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, ] [[package]] name = "jupyterlab-widgets" version = "3.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, ] [[package]] name = "kiwisolver" version = "1.4.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" }, - { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" }, - { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" }, - { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" }, - { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" }, - { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" }, - { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" }, - { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" }, - { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" }, - { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" }, - { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" }, - { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" }, - { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" }, - { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" }, - { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159 }, + { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578 }, + { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312 }, + { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458 }, + { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640 }, + { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074 }, + { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036 }, + { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310 }, + { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943 }, + { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488 }, + { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787 }, + { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730 }, + { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036 }, + { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183 }, + { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675 }, + { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277 }, + { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994 }, + { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744 }, ] [[package]] @@ -2100,7 +2111,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474, upload-time = "2021-05-07T07:54:13.562Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474 } [[package]] name = "langextract" @@ -2124,9 +2135,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029, upload-time = "2025-11-14T22:21:30.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029 } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592, upload-time = "2025-11-14T22:21:29.044Z" }, + { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592 }, ] [package.optional-dependencies] @@ -2138,9 +2149,9 @@ openai = [ name = "lark" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, ] [[package]] @@ -2150,9 +2161,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fire" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292, upload-time = "2022-09-06T16:09:06.607Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594, upload-time = "2022-09-06T16:09:04.658Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594 }, ] [[package]] @@ -2173,39 +2184,39 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976, upload-time = "2025-11-16T00:03:51.812Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975, upload-time = "2025-11-16T00:03:49.182Z" }, + { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975 }, ] [[package]] name = "lxml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589, upload-time = "2025-09-22T04:00:10.51Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671, upload-time = "2025-09-22T04:00:15.411Z" }, - { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961, upload-time = "2025-09-22T04:00:17.619Z" }, - { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087, upload-time = "2025-09-22T04:00:19.868Z" }, - { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620, upload-time = "2025-09-22T04:00:21.877Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664, upload-time = "2025-09-22T04:00:23.714Z" }, - { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397, upload-time = "2025-09-22T04:00:25.544Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178, upload-time = "2025-09-22T04:00:27.602Z" }, - { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148, upload-time = "2025-09-22T04:00:29.323Z" }, - { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035, upload-time = "2025-09-22T04:00:31.061Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111, upload-time = "2025-09-22T04:00:33.11Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662, upload-time = "2025-09-22T04:00:35.237Z" }, - { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973, upload-time = "2025-09-22T04:00:37.086Z" }, - { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953, upload-time = "2025-09-22T04:00:39.224Z" }, - { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695, upload-time = "2025-09-22T04:00:41.402Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051, upload-time = "2025-09-22T04:00:43.525Z" }, - { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264, upload-time = "2025-09-22T04:04:32.892Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435, upload-time = "2025-09-22T04:04:34.907Z" }, - { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913, upload-time = "2025-09-22T04:04:37.205Z" }, - { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357, upload-time = "2025-09-22T04:04:39.322Z" }, - { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295, upload-time = "2025-09-22T04:04:41.647Z" }, - { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913, upload-time = "2025-09-22T04:04:43.602Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589 }, + { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671 }, + { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961 }, + { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087 }, + { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620 }, + { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664 }, + { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397 }, + { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178 }, + { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148 }, + { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035 }, + { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111 }, + { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662 }, + { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973 }, + { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953 }, + { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695 }, + { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051 }, + { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264 }, + { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435 }, + { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913 }, + { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357 }, + { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295 }, + { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913 }, ] [[package]] @@ -2215,9 +2226,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, ] [[package]] @@ -2227,18 +2238,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, ] [[package]] name = "markdown2" version = "2.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652, upload-time = "2025-07-27T16:16:24.307Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954, upload-time = "2025-07-27T16:16:23.026Z" }, + { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954 }, ] [[package]] @@ -2249,9 +2260,9 @@ dependencies = [ { name = "beautifulsoup4" }, { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816, upload-time = "2025-11-16T19:21:18.565Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724, upload-time = "2025-11-16T19:21:17.622Z" }, + { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724 }, ] [[package]] @@ -2281,28 +2292,28 @@ dependencies = [ { name = "tqdm" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302, upload-time = "2025-09-30T15:44:59.591Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914, upload-time = "2025-09-30T15:44:58.441Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914 }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, - { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, - { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, - { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, - { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631 }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057 }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050 }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681 }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705 }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524 }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282 }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745 }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571 }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056 }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932 }, ] [[package]] @@ -2340,18 +2351,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110 } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516 }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] [[package]] @@ -2374,7 +2385,7 @@ dependencies = [ { name = "absl-py" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356, upload-time = "2025-04-17T08:25:02.247Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356 } wheels = [ { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, ] @@ -2401,7 +2412,7 @@ dependencies = [ { name = "scikit-learn" }, { name = "scipy" }, { name = "sqlalchemy" }, - { name = "waitress", marker = "platform_system == 'Windows'" }, + { name = "waitress", marker = "(platform_machine != 'aarch64' and platform_system == 'Windows' and sys_platform == 'linux') or (platform_system == 'Windows' and sys_platform != 'darwin' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ad/f8/10ccb111ed53732dfceae0369073023f96acd6b00f92fb3c24473938702d/mlflow-3.8.1.tar.gz", hash = "sha256:0823377bedff4d530b0d560bf394daf9f7e9fbba53453add04eadad34de962cc", size = 8550037 } wheels = [ @@ -2461,9 +2472,9 @@ wheels = [ name = "more-itertools" version = "10.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667 }, ] [[package]] @@ -2474,18 +2485,18 @@ dependencies = [ { name = "jinja2" }, { name = "matplotlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433, upload-time = "2025-11-05T18:12:24.183Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051, upload-time = "2025-11-05T18:12:22.527Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, ] [[package]] @@ -2495,27 +2506,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153, upload-time = "2025-10-06T14:48:26.409Z" }, - { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993, upload-time = "2025-10-06T14:48:28.4Z" }, - { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607, upload-time = "2025-10-06T14:48:29.581Z" }, - { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847, upload-time = "2025-10-06T14:48:32.107Z" }, - { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616, upload-time = "2025-10-06T14:48:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333, upload-time = "2025-10-06T14:48:35.9Z" }, - { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239, upload-time = "2025-10-06T14:48:37.302Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618, upload-time = "2025-10-06T14:48:38.963Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655, upload-time = "2025-10-06T14:48:40.312Z" }, - { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245, upload-time = "2025-10-06T14:48:41.848Z" }, - { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523, upload-time = "2025-10-06T14:48:43.749Z" }, - { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129, upload-time = "2025-10-06T14:48:45.225Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999, upload-time = "2025-10-06T14:48:46.703Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711, upload-time = "2025-10-06T14:48:48.146Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504, upload-time = "2025-10-06T14:48:49.447Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422, upload-time = "2025-10-06T14:48:50.789Z" }, - { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050, upload-time = "2025-10-06T14:48:51.938Z" }, - { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153, upload-time = "2025-10-06T14:48:53.146Z" }, - { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153 }, + { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993 }, + { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607 }, + { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847 }, + { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616 }, + { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333 }, + { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239 }, + { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618 }, + { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655 }, + { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245 }, + { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523 }, + { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129 }, + { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999 }, + { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711 }, + { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504 }, + { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422 }, + { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050 }, + { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153 }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317 }, ] [[package]] @@ -2525,14 +2536,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503, upload-time = "2025-04-17T03:11:27.742Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083, upload-time = "2025-04-17T03:11:04.223Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128, upload-time = "2025-04-17T03:11:06.045Z" }, - { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132, upload-time = "2025-04-17T03:11:07.533Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948, upload-time = "2025-04-17T03:11:20.223Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636, upload-time = "2025-04-17T03:11:24.936Z" }, - { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083 }, + { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128 }, + { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132 }, + { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948 }, + { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636 }, + { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478 }, ] [[package]] @@ -2579,9 +2590,9 @@ dependencies = [ { name = "pygments" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715, upload-time = "2025-01-28T09:29:14.724Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525, upload-time = "2025-01-28T09:29:12.551Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525 }, ] [[package]] @@ -2594,9 +2605,9 @@ dependencies = [ { name = "jupyter-core" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, ] [[package]] @@ -2606,27 +2617,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nbformat" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747, upload-time = "2025-11-16T17:38:55.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122, upload-time = "2025-11-16T17:38:54.164Z" }, + { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122 }, ] [[package]] name = "nest-asyncio" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, ] [[package]] name = "networkx" version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, ] [[package]] @@ -2661,25 +2672,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, ] [[package]] @@ -2687,7 +2698,8 @@ name = "nvidia-cublas-cu12" version = "12.8.4.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, + { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, ] [[package]] @@ -2695,7 +2707,8 @@ name = "nvidia-cuda-cupti-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, ] [[package]] @@ -2703,7 +2716,8 @@ name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, + { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, ] [[package]] @@ -2711,7 +2725,8 @@ name = "nvidia-cuda-runtime-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, ] [[package]] @@ -2722,7 +2737,8 @@ dependencies = [ { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, + { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, ] [[package]] @@ -2733,7 +2749,8 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, + { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, ] [[package]] @@ -2741,7 +2758,8 @@ name = "nvidia-cufile-cu12" version = "1.13.1.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, + { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, ] [[package]] @@ -2749,7 +2767,8 @@ name = "nvidia-curand-cu12" version = "10.3.9.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, + { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, ] [[package]] @@ -2762,7 +2781,8 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, + { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, ] [[package]] @@ -2773,7 +2793,8 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, ] [[package]] @@ -2781,7 +2802,8 @@ name = "nvidia-cusparselt-cu12" version = "0.7.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, + { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, ] [[package]] @@ -2789,7 +2811,8 @@ name = "nvidia-nccl-cu12" version = "2.27.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, ] [[package]] @@ -2797,7 +2820,8 @@ name = "nvidia-nvjitlink-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, + { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, ] [[package]] @@ -2805,7 +2829,8 @@ name = "nvidia-nvshmem-cu12" version = "3.3.20" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" }, + { url = "https://files.pythonhosted.org/packages/92/9d/3dd98852568fb845ec1f7902c90a22b240fe1cbabda411ccedf2fd737b7b/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b0b960da3842212758e4fa4696b94f129090b30e5122fea3c5345916545cff0", size = 124484616 }, + { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, ] [[package]] @@ -2813,7 +2838,8 @@ name = "nvidia-nvtx-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, ] [[package]] @@ -2823,7 +2849,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045, upload-time = "2020-01-18T16:55:48.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045 } [[package]] name = "ollama" @@ -2833,9 +2859,9 @@ dependencies = [ { name = "httpx" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620, upload-time = "2025-11-13T23:02:17.416Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620 } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354, upload-time = "2025-11-13T23:02:16.292Z" }, + { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354 }, ] [[package]] @@ -2852,9 +2878,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133, upload-time = "2025-09-24T13:00:53.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627, upload-time = "2025-09-24T13:00:50.754Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627 }, ] [[package]] @@ -2864,7 +2890,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } wheels = [ { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, @@ -2939,9 +2965,9 @@ dependencies = [ { name = "sqlalchemy" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444, upload-time = "2025-11-10T05:14:30.151Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708, upload-time = "2025-11-10T05:14:28.6Z" }, + { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708 }, ] [[package]] @@ -2969,18 +2995,18 @@ wheels = [ name = "overrides" version = "7.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] [[package]] @@ -3008,18 +3034,18 @@ wheels = [ name = "pandocfilters" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, ] [[package]] name = "parso" version = "0.8.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205 } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668 }, ] [[package]] @@ -3032,9 +3058,9 @@ dependencies = [ { name = "pydantic-settings" }, { name = "pypdfium2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968, upload-time = "2025-06-11T14:42:09.492Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693, upload-time = "2025-06-11T14:42:08.157Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693 }, ] [[package]] @@ -3044,53 +3070,53 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, ] [[package]] name = "pillow" version = "10.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload-time = "2024-07-01T09:45:22.07Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload-time = "2024-07-01T09:45:25.292Z" }, - { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload-time = "2024-07-01T09:45:27.94Z" }, - { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload-time = "2024-07-01T09:45:30.305Z" }, - { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload-time = "2024-07-01T09:45:32.868Z" }, - { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload-time = "2024-07-01T09:45:35.279Z" }, - { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload-time = "2024-07-01T09:45:37.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload-time = "2024-07-01T09:45:39.89Z" }, - { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload-time = "2024-07-01T09:45:42.771Z" }, - { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload-time = "2024-07-01T09:45:45.176Z" }, - { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload-time = "2024-07-01T09:45:47.274Z" }, - { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload-time = "2024-07-01T09:48:04.815Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload-time = "2024-07-01T09:48:07.206Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload-time = "2024-07-01T09:48:09.66Z" }, - { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload-time = "2024-07-01T09:48:12.529Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload-time = "2024-07-01T09:48:14.891Z" }, - { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload-time = "2024-07-01T09:48:17.601Z" }, - { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload-time = "2024-07-01T09:48:20.293Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, ] [[package]] name = "pip" version = "25.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014 } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" }, + { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622 }, ] [[package]] name = "platformdirs" version = "4.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731 }, ] [[package]] @@ -3102,14 +3128,14 @@ dependencies = [ { name = "pyee" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424, upload-time = "2025-11-11T18:39:10.175Z" }, - { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228, upload-time = "2025-11-11T18:39:13.915Z" }, - { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424, upload-time = "2025-11-11T18:39:17.093Z" }, - { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122, upload-time = "2025-11-11T18:39:20.619Z" }, - { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645, upload-time = "2025-11-11T18:39:24.005Z" }, - { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837, upload-time = "2025-11-11T18:39:27.174Z" }, - { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843, upload-time = "2025-11-11T18:39:30.851Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959, upload-time = "2025-11-11T18:39:33.998Z" }, + { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424 }, + { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228 }, + { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424 }, + { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122 }, + { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645 }, + { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837 }, + { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843 }, + { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959 }, ] [[package]] @@ -3120,16 +3146,16 @@ dependencies = [ { name = "narwhals" }, { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624, upload-time = "2025-11-17T18:39:24.523Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174, upload-time = "2025-11-17T18:39:20.351Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174 }, ] [[package]] name = "pptree" version = "3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043, upload-time = "2020-04-15T18:28:53.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043 } [[package]] name = "pre-commit" @@ -3151,9 +3177,9 @@ wheels = [ name = "prometheus-client" version = "0.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481, upload-time = "2025-09-18T20:47:25.043Z" } +sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, + { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145 }, ] [[package]] @@ -3163,33 +3189,33 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, ] [[package]] name = "propcache" version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, - { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, - { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, - { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, - { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, - { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, - { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, - { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, - { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, - { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534 }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526 }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263 }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012 }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491 }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319 }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856 }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241 }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552 }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778 }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047 }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093 }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638 }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229 }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, ] [[package]] @@ -3223,57 +3249,57 @@ wheels = [ name = "psutil" version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565, upload-time = "2024-10-17T21:31:45.68Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565 } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762, upload-time = "2024-10-17T21:32:05.991Z" }, - { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777, upload-time = "2024-10-17T21:32:07.872Z" }, - { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259, upload-time = "2024-10-17T21:32:10.177Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255, upload-time = "2024-10-17T21:32:11.964Z" }, - { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804, upload-time = "2024-10-17T21:32:13.785Z" }, - { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386, upload-time = "2024-10-17T21:32:21.399Z" }, - { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228, upload-time = "2024-10-17T21:32:23.88Z" }, + { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762 }, + { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777 }, + { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259 }, + { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255 }, + { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804 }, + { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386 }, + { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228 }, ] [[package]] name = "ptyprocess" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, ] [[package]] name = "pure-eval" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, ] [[package]] name = "pyarrow" version = "22.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151, upload-time = "2025-10-24T12:30:00.762Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968, upload-time = "2025-10-24T10:03:31.21Z" }, - { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085, upload-time = "2025-10-24T10:03:38.146Z" }, - { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613, upload-time = "2025-10-24T10:03:46.516Z" }, - { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059, upload-time = "2025-10-24T10:03:55.353Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043, upload-time = "2025-10-24T10:04:05.408Z" }, - { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505, upload-time = "2025-10-24T10:04:15.786Z" }, - { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641, upload-time = "2025-10-24T10:04:22.57Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968 }, + { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085 }, + { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613 }, + { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059 }, + { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043 }, + { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505 }, + { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641 }, ] [[package]] name = "pyasn1" version = "0.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, ] [[package]] @@ -3283,18 +3309,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, ] [[package]] name = "pycparser" version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, ] [[package]] @@ -3307,9 +3333,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 }, ] [package.optional-dependencies] @@ -3324,7 +3350,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 } wheels = [ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298 }, { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475 }, @@ -3379,9 +3405,9 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880 }, ] [[package]] @@ -3392,9 +3418,9 @@ dependencies = [ { name = "jinja2" }, { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, ] [[package]] @@ -3404,18 +3430,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730 }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] [[package]] @@ -3450,9 +3476,9 @@ wheels = [ name = "pypandoc" version = "1.16.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477, upload-time = "2025-11-13T16:30:29.608Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451, upload-time = "2025-11-13T16:30:07.66Z" }, + { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451 }, ] [[package]] @@ -3468,29 +3494,29 @@ wheels = [ name = "pypdfium2" version = "4.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239, upload-time = "2024-05-09T18:33:17.552Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254, upload-time = "2024-05-09T18:32:48.653Z" }, - { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624, upload-time = "2024-05-09T18:32:51.458Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126, upload-time = "2024-05-09T18:32:53.581Z" }, - { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077, upload-time = "2024-05-09T18:32:55.99Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431, upload-time = "2024-05-09T18:32:57.911Z" }, - { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008, upload-time = "2024-05-09T18:32:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543, upload-time = "2024-05-09T18:33:02.597Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911, upload-time = "2024-05-09T18:33:05.376Z" }, - { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430, upload-time = "2024-05-09T18:33:08.067Z" }, - { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951, upload-time = "2024-05-09T18:33:10.567Z" }, - { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098, upload-time = "2024-05-09T18:33:13.107Z" }, - { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118, upload-time = "2024-05-09T18:33:15.489Z" }, + { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254 }, + { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624 }, + { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126 }, + { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077 }, + { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431 }, + { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008 }, + { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543 }, + { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911 }, + { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430 }, + { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951 }, + { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098 }, + { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118 }, ] [[package]] name = "pysocks" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725 }, ] [[package]] @@ -3500,9 +3526,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] [[package]] @@ -3513,27 +3539,27 @@ dependencies = [ { name = "lxml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987 }, ] [[package]] name = "python-dotenv" version = "1.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, ] [[package]] name = "python-json-logger" version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548 }, ] [[package]] @@ -3561,9 +3587,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889, upload-time = "2022-11-25T19:33:07.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949, upload-time = "2022-11-25T19:33:06.093Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949 }, ] [[package]] @@ -3574,18 +3600,18 @@ dependencies = [ { name = "numpy" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086, upload-time = "2021-01-09T17:35:49.131Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564, upload-time = "2021-01-09T17:35:47.543Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564 }, ] [[package]] name = "pytz" version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, ] [[package]] @@ -3593,35 +3619,35 @@ name = "pywin32" version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432 }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103 }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557 }, ] [[package]] name = "pywinpty" version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669, upload-time = "2025-10-03T21:16:29.205Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330, upload-time = "2025-10-03T21:20:15.656Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330 }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227 }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019 }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646 }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793 }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293 }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872 }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828 }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415 }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561 }, ] [[package]] @@ -3631,52 +3657,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850, upload-time = "2025-09-08T23:07:26.274Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380, upload-time = "2025-09-08T23:07:29.78Z" }, - { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421, upload-time = "2025-09-08T23:07:31.263Z" }, - { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149, upload-time = "2025-09-08T23:07:33.17Z" }, - { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070, upload-time = "2025-09-08T23:07:35.205Z" }, - { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441, upload-time = "2025-09-08T23:07:37.432Z" }, - { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529, upload-time = "2025-09-08T23:07:39.047Z" }, - { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276, upload-time = "2025-09-08T23:07:40.695Z" }, - { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208, upload-time = "2025-09-08T23:07:42.298Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766, upload-time = "2025-09-08T23:07:43.869Z" }, - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, - { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266, upload-time = "2025-09-08T23:09:40.048Z" }, - { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206, upload-time = "2025-09-08T23:09:41.902Z" }, - { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747, upload-time = "2025-09-08T23:09:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371, upload-time = "2025-09-08T23:09:45.575Z" }, - { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862, upload-time = "2025-09-08T23:09:47.448Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850 }, + { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380 }, + { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421 }, + { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149 }, + { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070 }, + { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441 }, + { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529 }, + { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276 }, + { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208 }, + { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766 }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, + { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266 }, + { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206 }, + { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747 }, + { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371 }, + { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862 }, ] [[package]] name = "rapidfuzz" version = "3.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900, upload-time = "2025-11-01T11:54:52.321Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376, upload-time = "2025-11-01T11:52:29.175Z" }, - { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903, upload-time = "2025-11-01T11:52:31.239Z" }, - { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655, upload-time = "2025-11-01T11:52:32.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708, upload-time = "2025-11-01T11:52:34.618Z" }, - { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106, upload-time = "2025-11-01T11:52:36.069Z" }, - { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048, upload-time = "2025-11-01T11:52:37.936Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020, upload-time = "2025-11-01T11:52:39.657Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958, upload-time = "2025-11-01T11:52:41.017Z" }, - { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043, upload-time = "2025-11-01T11:52:42.465Z" }, - { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273, upload-time = "2025-11-01T11:52:44.005Z" }, - { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875, upload-time = "2025-11-01T11:52:45.405Z" }, + { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376 }, + { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903 }, + { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655 }, + { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708 }, + { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106 }, + { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048 }, + { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020 }, + { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958 }, + { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043 }, + { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273 }, + { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875 }, ] [[package]] @@ -3688,33 +3714,33 @@ dependencies = [ { name = "rpds-py" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, ] [[package]] name = "regex" version = "2024.11.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674, upload-time = "2024-11-06T20:08:57.575Z" }, - { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684, upload-time = "2024-11-06T20:08:59.787Z" }, - { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589, upload-time = "2024-11-06T20:09:01.896Z" }, - { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511, upload-time = "2024-11-06T20:09:04.062Z" }, - { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149, upload-time = "2024-11-06T20:09:06.237Z" }, - { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707, upload-time = "2024-11-06T20:09:07.715Z" }, - { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702, upload-time = "2024-11-06T20:09:10.101Z" }, - { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976, upload-time = "2024-11-06T20:09:11.566Z" }, - { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397, upload-time = "2024-11-06T20:09:13.119Z" }, - { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726, upload-time = "2024-11-06T20:09:14.85Z" }, - { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098, upload-time = "2024-11-06T20:09:16.504Z" }, - { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325, upload-time = "2024-11-06T20:09:18.698Z" }, - { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277, upload-time = "2024-11-06T20:09:21.725Z" }, - { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197, upload-time = "2024-11-06T20:09:24.092Z" }, - { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714, upload-time = "2024-11-06T20:09:26.36Z" }, - { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042, upload-time = "2024-11-06T20:09:28.762Z" }, + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, ] [[package]] @@ -3727,9 +3753,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, ] [package.optional-dependencies] @@ -3744,18 +3770,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, ] [[package]] name = "rfc3986-validator" version = "0.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, ] [[package]] @@ -3765,9 +3791,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lark" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, ] [[package]] @@ -3778,9 +3804,9 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393 }, ] [[package]] @@ -3801,56 +3827,56 @@ wheels = [ name = "rignore" version = "0.7.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999, upload-time = "2025-11-05T20:42:38.373Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824, upload-time = "2025-11-05T20:42:22.1Z" }, - { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121, upload-time = "2025-11-05T20:40:48.94Z" }, - { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813, upload-time = "2025-11-05T20:41:04.71Z" }, - { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019, upload-time = "2025-11-05T20:41:20.723Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822, upload-time = "2025-11-05T20:41:36.99Z" }, - { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820, upload-time = "2025-11-05T20:42:06.364Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050, upload-time = "2025-11-05T20:41:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164, upload-time = "2025-11-05T21:40:10.368Z" }, - { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028, upload-time = "2025-11-05T21:40:27.977Z" }, - { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024, upload-time = "2025-11-05T21:40:45.148Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531, upload-time = "2025-11-05T21:41:02.734Z" }, - { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817, upload-time = "2025-11-05T21:41:47.51Z" }, - { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001, upload-time = "2025-11-05T21:41:32.778Z" }, - { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462, upload-time = "2025-11-05T20:42:50.804Z" }, - { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918, upload-time = "2025-11-05T20:42:33.689Z" }, - { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922, upload-time = "2025-11-05T20:41:00.361Z" }, - { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987, upload-time = "2025-11-05T20:41:16.219Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110, upload-time = "2025-11-05T20:41:32.631Z" }, - { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339, upload-time = "2025-11-05T20:41:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680, upload-time = "2025-11-05T20:42:18.061Z" }, - { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045, upload-time = "2025-11-05T20:42:02.315Z" }, - { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310, upload-time = "2025-11-05T21:40:23.184Z" }, - { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998, upload-time = "2025-11-05T21:40:40.603Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178, upload-time = "2025-11-05T21:40:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190, upload-time = "2025-11-05T21:41:16.403Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999 }, + { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824 }, + { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121 }, + { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813 }, + { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019 }, + { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822 }, + { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820 }, + { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050 }, + { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164 }, + { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028 }, + { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024 }, + { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531 }, + { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817 }, + { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001 }, + { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462 }, + { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918 }, + { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922 }, + { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987 }, + { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110 }, + { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339 }, + { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680 }, + { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045 }, + { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310 }, + { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998 }, + { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178 }, + { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190 }, ] [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, - { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, - { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, - { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, - { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, - { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, - { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, - { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, - { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, - { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, - { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, - { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, - { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490 }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751 }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696 }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136 }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699 }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022 }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522 }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579 }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305 }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503 }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322 }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792 }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901 }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823 }, ] [[package]] @@ -3860,9 +3886,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 }, ] [[package]] @@ -3872,35 +3898,35 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830 }, ] [[package]] name = "safetensors" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, - { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, - { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, - { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430 }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977 }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890 }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885 }, ] [[package]] @@ -3913,13 +3939,13 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, - { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, - { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, - { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221 }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834 }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938 }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818 }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969 }, ] [[package]] @@ -3929,16 +3955,16 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870, upload-time = "2024-06-24T20:35:18.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226, upload-time = "2024-06-24T20:31:50.451Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893, upload-time = "2024-06-24T20:31:57.337Z" }, - { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258, upload-time = "2024-06-24T20:32:02.711Z" }, - { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715, upload-time = "2024-06-24T20:32:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038, upload-time = "2024-06-24T20:32:17.305Z" }, - { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959, upload-time = "2024-06-24T20:32:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514, upload-time = "2024-06-24T20:32:32.618Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252, upload-time = "2024-06-24T20:32:45.06Z" }, + { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226 }, + { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893 }, + { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258 }, + { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715 }, + { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038 }, + { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959 }, + { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514 }, + { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252 }, ] [[package]] @@ -3950,9 +3976,9 @@ dependencies = [ { name = "numpy" }, { name = "pandas" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, ] [[package]] @@ -3962,9 +3988,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "regex" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244, upload-time = "2021-12-15T21:56:14.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332, upload-time = "2021-12-15T21:56:12.508Z" }, + { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332 }, ] [[package]] @@ -3998,16 +4024,16 @@ wheels = [ name = "sentencepiece" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106, upload-time = "2024-02-19T17:06:47.428Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979, upload-time = "2024-02-19T17:05:34.651Z" }, - { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845, upload-time = "2024-02-19T17:05:37.371Z" }, - { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472, upload-time = "2024-02-19T17:05:39.775Z" }, - { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151, upload-time = "2024-02-19T17:05:42.594Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931, upload-time = "2024-02-19T17:05:44.695Z" }, - { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537, upload-time = "2024-02-19T17:05:46.713Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747, upload-time = "2024-02-19T17:05:48.705Z" }, - { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525, upload-time = "2024-02-19T17:05:55.145Z" }, + { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979 }, + { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845 }, + { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472 }, + { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151 }, + { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931 }, + { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537 }, + { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747 }, + { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525 }, ] [[package]] @@ -4027,54 +4053,54 @@ wheels = [ name = "setuptools" version = "80.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486 }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] [[package]] name = "smmap" version = "5.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, ] [[package]] @@ -4110,7 +4136,7 @@ wheels = [ name = "sqlitedict" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846, upload-time = "2022-12-03T13:39:13.102Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846 } [[package]] name = "sqlmodel" @@ -4120,7 +4146,7 @@ dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392, upload-time = "2024-08-31T09:43:24.088Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392 } wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, ] @@ -4143,9 +4169,9 @@ dependencies = [ { name = "executing" }, { name = "pure-eval" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, ] [[package]] @@ -4156,9 +4182,9 @@ dependencies = [ { name = "anyio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, + { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033 }, ] [[package]] @@ -4209,9 +4235,9 @@ dependencies = [ { name = "torch" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481, upload-time = "2025-09-23T21:41:37.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395, upload-time = "2025-09-23T21:41:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395 }, ] [[package]] @@ -4221,27 +4247,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, ] [[package]] name = "tabulate" version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, ] [[package]] name = "tenacity" version = "9.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248 }, ] [[package]] @@ -4273,21 +4299,21 @@ version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, ] [[package]] name = "threadpoolctl" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, ] [[package]] @@ -4298,15 +4324,15 @@ dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991, upload-time = "2025-10-06T20:21:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798, upload-time = "2025-10-06T20:21:35.579Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865, upload-time = "2025-10-06T20:21:36.675Z" }, - { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856, upload-time = "2025-10-06T20:21:37.873Z" }, - { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308, upload-time = "2025-10-06T20:21:39.577Z" }, - { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697, upload-time = "2025-10-06T20:21:41.154Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375, upload-time = "2025-10-06T20:21:43.201Z" }, + { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991 }, + { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798 }, + { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865 }, + { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856 }, + { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308 }, + { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697 }, + { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375 }, ] [[package]] @@ -4316,9 +4342,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, ] [[package]] @@ -4355,18 +4381,18 @@ wheels = [ name = "toml" version = "0.10.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, ] [[package]] name = "tomli" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408 }, ] [[package]] @@ -4378,30 +4404,30 @@ dependencies = [ { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681, upload-time = "2025-11-12T15:19:56.48Z" }, - { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036, upload-time = "2025-11-12T15:21:01.886Z" }, - { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861, upload-time = "2025-11-12T15:21:30.145Z" }, - { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222, upload-time = "2025-11-12T15:20:46.223Z" }, + { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681 }, + { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036 }, + { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861 }, + { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222 }, ] [[package]] @@ -4413,9 +4439,9 @@ dependencies = [ { name = "packaging" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144, upload-time = "2023-03-10T22:02:20.455Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162, upload-time = "2023-03-10T22:02:18.818Z" }, + { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162 }, ] [[package]] @@ -4442,20 +4468,20 @@ name = "tqdm" version = "4.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Windows' and sys_platform == 'linux') or (platform_system == 'Windows' and sys_platform != 'darwin' and sys_platform != 'linux')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, ] [[package]] name = "traitlets" version = "5.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, ] [[package]] @@ -4466,9 +4492,9 @@ dependencies = [ { name = "torch" }, { name = "transformers", extra = ["sentencepiece", "torch"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754, upload-time = "2025-06-15T13:34:38.522Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073, upload-time = "2025-06-15T13:34:37.468Z" }, + { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073 }, ] [[package]] @@ -4487,9 +4513,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680, upload-time = "2025-11-25T15:51:30.139Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463, upload-time = "2025-11-25T15:51:26.493Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463 }, ] [package.optional-dependencies] @@ -4507,7 +4533,8 @@ name = "triton" version = "3.5.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692, upload-time = "2025-11-11T17:40:46.074Z" }, + { url = "https://files.pythonhosted.org/packages/d9/2e/f95e673222afa2c7f0c687d8913e98fcf2589ef0b1405de76894e37fe18f/triton-3.5.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f63e34dcb32d7bd3a1d0195f60f30d2aee8b08a69a0424189b71017e23dfc3d2", size = 159821655 }, + { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692 }, ] [[package]] @@ -4529,9 +4556,9 @@ wheels = [ name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, ] [[package]] @@ -4541,9 +4568,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, ] [[package]] @@ -4559,18 +4586,18 @@ wheels = [ name = "unidecode" version = "1.3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701, upload-time = "2024-01-11T11:58:35.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494, upload-time = "2024-01-11T11:58:33.012Z" }, + { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494 }, ] [[package]] name = "uri-template" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, ] [[package]] @@ -4598,7 +4625,7 @@ wheels = [ [package.optional-dependencies] standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, { name = "httptools" }, { name = "python-dotenv" }, { name = "pyyaml" }, @@ -4611,14 +4638,14 @@ standard = [ name = "uvloop" version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" }, - { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" }, - { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" }, - { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" }, + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335 }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903 }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499 }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133 }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681 }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261 }, ] [[package]] @@ -4631,7 +4658,7 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799 } wheels = [ { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095 }, ] @@ -4649,18 +4676,18 @@ wheels = [ name = "watchdog" version = "6.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, ] [[package]] @@ -4670,67 +4697,67 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" }, - { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" }, - { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" }, - { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" }, - { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" }, - { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" }, - { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" }, - { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" }, - { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" }, - { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" }, - { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" }, - { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318 }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478 }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894 }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065 }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377 }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837 }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456 }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614 }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690 }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459 }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663 }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453 }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611 }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889 }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616 }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413 }, ] [[package]] name = "wcwidth" version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293 } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286 }, ] [[package]] name = "webcolors" version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, ] [[package]] name = "webencodings" version = "0.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, ] [[package]] name = "websocket-client" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, ] [[package]] name = "websockets" version = "15.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } wheels = [ { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423 }, { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080 }, @@ -4768,9 +4795,9 @@ wheels = [ name = "widgetsnbextension" version = "4.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, ] [[package]] @@ -4780,59 +4807,59 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581480705f912040172f6570cc12e68a245ba9258c32ef/wikipedia_api-0.8.1.tar.gz", hash = "sha256:b31e93b3f5407c1a1ba413ed7326a05379a3c270df6cf6a211aca67a14c5658b", size = 19934, upload-time = "2025-01-19T23:44:33.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581480705f912040172f6570cc12e68a245ba9258c32ef/wikipedia_api-0.8.1.tar.gz", hash = "sha256:b31e93b3f5407c1a1ba413ed7326a05379a3c270df6cf6a211aca67a14c5658b", size = 19934 } [[package]] name = "wrapt" version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481, upload-time = "2025-11-07T00:43:11.103Z" }, - { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692, upload-time = "2025-11-07T00:43:13.697Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574, upload-time = "2025-11-07T00:43:14.967Z" }, - { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688, upload-time = "2025-11-07T00:43:18.275Z" }, - { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698, upload-time = "2025-11-07T00:43:19.407Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096, upload-time = "2025-11-07T00:43:16.5Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878, upload-time = "2025-11-07T00:43:20.81Z" }, - { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298, upload-time = "2025-11-07T00:43:22.229Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361, upload-time = "2025-11-07T00:43:24.301Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035, upload-time = "2025-11-07T00:43:28.96Z" }, - { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383, upload-time = "2025-11-07T00:43:25.804Z" }, - { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894, upload-time = "2025-11-07T00:43:27.074Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, + { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481 }, + { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692 }, + { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574 }, + { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688 }, + { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698 }, + { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096 }, + { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878 }, + { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298 }, + { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361 }, + { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035 }, + { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383 }, + { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894 }, + { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046 }, ] [[package]] name = "xmltodict" version = "0.14.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942, upload-time = "2024-10-16T06:10:29.683Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981, upload-time = "2024-10-16T06:10:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981 }, ] [[package]] name = "xxhash" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" }, - { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" }, - { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" }, - { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" }, - { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" }, - { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" }, - { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" }, - { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" }, - { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" }, - { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" }, - { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" }, - { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" }, + { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845 }, + { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807 }, + { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786 }, + { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830 }, + { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606 }, + { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872 }, + { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217 }, + { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139 }, + { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669 }, + { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018 }, + { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058 }, + { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628 }, + { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577 }, + { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487 }, + { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863 }, ] [[package]] @@ -4844,32 +4871,32 @@ dependencies = [ { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, - { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, - { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, - { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, - { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, - { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, - { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, - { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, - { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, - { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, - { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, - { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, - { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, - { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, - { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, - { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517 }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495 }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400 }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545 }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598 }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893 }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240 }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965 }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026 }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637 }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082 }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811 }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223 }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118 }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852 }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012 }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814 }, ] [[package]] name = "zipp" version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, ] From b34374613a0aa8e99272edf8d5a8648d1e3c6c0d Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Tue, 10 Feb 2026 19:42:00 -0300 Subject: [PATCH 033/101] Hotfix: resolve file pathing, logic indentation, and date disambiguation - Update configuration path in llm.py from .yaml to .yml. - Fix indentation in core.py for canonical_entity_id assignment. This ensures all predictions receive an ID even if they lack a canonical match, bypassing the 'aymurai_label_subclass == 0' filter which caused issues with date formatting in NER post-processing. - Add condition in anonymizer.py to trigger 'get_canonical_dates' only when FECHA is present in 'fuzzy_labels'. This prevents unintended date disambiguation when the policy is set to None. --- .../routers/anonymizer/anonymizer.py | 3 +- aymurai/utils/entity_disambiguation/core.py | 18 +- aymurai/utils/entity_disambiguation/llm.py | 2 +- .../08-disambiguation-endpoint-smoke.ipynb | 20 +- uv.lock | 2731 ++++++++--------- 5 files changed, 1363 insertions(+), 1411 deletions(-) diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index dc7f3d56..ec2b481e 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -327,7 +327,8 @@ async def anonymizer_disambiguate( else [] ) - canonical_entities += get_canonical_dates(labels=labels) + if "FECHA" in fuzzy_labels: + canonical_entities += get_canonical_dates(labels) logger.info( "fuzzy clustering produced %d canonical entities", len(canonical_entities) diff --git a/aymurai/utils/entity_disambiguation/core.py b/aymurai/utils/entity_disambiguation/core.py index 085d94de..22982c6c 100644 --- a/aymurai/utils/entity_disambiguation/core.py +++ b/aymurai/utils/entity_disambiguation/core.py @@ -112,15 +112,15 @@ def map_canonical_entities_ner_preds( label.attrs.aymurai_label_subclass.append(role) break - if label.attrs.canonical_entity_id is None: - key = ( - label.attrs.aymurai_label, - str(label.attrs.aymurai_alt_text).strip(), - ) - if key not in new_ids_map: - new_ids_map[key] = uuid.uuid4() - - label.attrs.canonical_entity_id = new_ids_map[key] + elif label.attrs.canonical_entity_id is None: + key = ( + label.attrs.aymurai_label, + str(label.attrs.aymurai_alt_text).strip(), + ) + if key not in new_ids_map: + new_ids_map[key] = uuid.uuid4() + + label.attrs.canonical_entity_id = new_ids_map[key] if include_label_instances: return assign_label_instances(predictions_mapped) diff --git a/aymurai/utils/entity_disambiguation/llm.py b/aymurai/utils/entity_disambiguation/llm.py index f3cc3fb7..6121d40c 100644 --- a/aymurai/utils/entity_disambiguation/llm.py +++ b/aymurai/utils/entity_disambiguation/llm.py @@ -27,7 +27,7 @@ def load_prompts_from_yaml(): Returns: PromptLibrary: The loaded prompt library. """ - path = Path(settings.RESOURCES_BASEPATH) / "llm/entity_disambiguation.yaml" + path = Path(settings.RESOURCES_BASEPATH) / "llm/entity_disambiguation.yml" data = load_yaml(file_path=str(path)) prompts = [] diff --git a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb index ceaa9f84..8ad00ae4 100644 --- a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb +++ b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb @@ -143,12 +143,6 @@ "\"\"\"" ] }, - { - "cell_type": "markdown", - "id": "61fb70d5", - "metadata": {}, - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -322,7 +316,7 @@ "metadata": {}, "outputs": [], "source": [ - "for doc_path in documents[2:3]:\n", + "for doc_path in documents[1:2]:\n", " print(f\"\\n=== {doc_path.name} ===\")\n", " session = requests.Session()\n", " document = extract_document(\n", @@ -381,19 +375,11 @@ " attrs.get(\"canonical_entity_id\"),\n", " )" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0f95a791", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "aymurai (3.10.19)", + "display_name": "aymurai", "language": "python", "name": "python3" }, @@ -407,7 +393,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.19" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/uv.lock b/uv.lock index a903aa6d..2adadb0f 100644 --- a/uv.lock +++ b/uv.lock @@ -1,22 +1,20 @@ version = 1 +revision = 3 requires-python = "==3.10.*" resolution-markers = [ - "platform_system == 'Darwin' and sys_platform == 'darwin'", - "platform_system != 'Darwin' and sys_platform == 'darwin'", - "platform_machine == 'aarch64' and platform_system == 'Linux' and sys_platform == 'linux'", - "platform_machine == 'aarch64' and platform_system != 'Linux' and sys_platform == 'linux'", - "(platform_machine != 'aarch64' and platform_system == 'Darwin' and sys_platform == 'linux') or (platform_system == 'Darwin' and sys_platform != 'darwin' and sys_platform != 'linux')", - "platform_machine == 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux'", - "(platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')", + "sys_platform == 'darwin'", + "python_version < '0'", + "platform_machine == 'aarch64' and sys_platform == 'linux'", + "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", ] [[package]] name = "absl-py" version = "2.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588 } +sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811 }, + { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" }, ] [[package]] @@ -32,18 +30,18 @@ dependencies = [ { name = "safetensors" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/8e/ac2a9566747a93f8be36ee08532eb0160558b07630a081a6056a9f89bf1d/accelerate-1.12.0.tar.gz", hash = "sha256:70988c352feb481887077d2ab845125024b2a137a5090d6d7a32b57d03a45df6", size = 398399 } +sdist = { url = "https://files.pythonhosted.org/packages/4a/8e/ac2a9566747a93f8be36ee08532eb0160558b07630a081a6056a9f89bf1d/accelerate-1.12.0.tar.gz", hash = "sha256:70988c352feb481887077d2ab845125024b2a137a5090d6d7a32b57d03a45df6", size = 398399, upload-time = "2025-11-21T11:27:46.973Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl", hash = "sha256:3e2091cd341423207e2f084a6654b1efcd250dc326f2a37d6dde446e07cabb11", size = 380935 }, + { url = "https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl", hash = "sha256:3e2091cd341423207e2f084a6654b1efcd250dc326f2a37d6dde446e07cabb11", size = 380935, upload-time = "2025-11-21T11:27:44.522Z" }, ] [[package]] name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] [[package]] @@ -60,25 +58,25 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556 } +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950 }, - { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099 }, - { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072 }, - { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588 }, - { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334 }, - { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656 }, - { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625 }, - { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604 }, - { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370 }, - { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023 }, - { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680 }, - { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407 }, - { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047 }, - { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264 }, - { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275 }, - { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053 }, - { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687 }, + { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, + { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, + { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, + { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, + { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, + { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, + { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, + { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, + { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, ] [[package]] @@ -89,9 +87,9 @@ dependencies = [ { name = "frozenlist" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] [[package]] @@ -104,9 +102,9 @@ dependencies = [ { name = "tomli" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064 } +sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064, upload-time = "2025-11-14T20:35:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554 }, + { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554, upload-time = "2025-11-14T20:35:05.699Z" }, ] [[package]] @@ -120,27 +118,27 @@ dependencies = [ { name = "packaging" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834 } +sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834, upload-time = "2025-11-12T08:59:11.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/33/ef2f2409450ef6daa61459d5de5c08128e7d3edb773fefd0a324d1310238/altair-6.0.0-py3-none-any.whl", hash = "sha256:09ae95b53d5fe5b16987dccc785a7af8588f2dca50de1e7a156efa8a461515f8", size = 795410 }, + { url = "https://files.pythonhosted.org/packages/db/33/ef2f2409450ef6daa61459d5de5c08128e7d3edb773fefd0a324d1310238/altair-6.0.0-py3-none-any.whl", hash = "sha256:09ae95b53d5fe5b16987dccc785a7af8588f2dca50de1e7a156efa8a461515f8", size = 795410, upload-time = "2025-11-12T08:59:09.804Z" }, ] [[package]] name = "annotated-doc" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] @@ -156,9 +154,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191, upload-time = "2025-02-18T20:35:33.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228 }, + { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228, upload-time = "2025-02-18T20:35:28.659Z" }, ] [[package]] @@ -170,18 +168,18 @@ dependencies = [ { name = "idna" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] [[package]] name = "appnope" version = "0.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, ] [[package]] @@ -191,9 +189,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, ] [[package]] @@ -203,23 +201,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, - { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549 }, - { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539 }, - { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467 }, - { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355 }, - { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, + { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549, upload-time = "2025-07-30T10:02:00.101Z" }, + { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539, upload-time = "2025-07-30T10:02:00.929Z" }, + { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467, upload-time = "2025-07-30T10:02:02.08Z" }, + { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355, upload-time = "2025-07-30T10:02:02.867Z" }, + { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187, upload-time = "2025-07-30T10:02:03.674Z" }, ] [[package]] @@ -230,18 +228,18 @@ dependencies = [ { name = "python-dateutil" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, ] [[package]] name = "asttokens" version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] [[package]] @@ -251,18 +249,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380 } +sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380, upload-time = "2025-03-16T17:25:36.919Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069 }, + { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069, upload-time = "2025-03-16T17:25:35.422Z" }, ] [[package]] name = "async-timeout" version = "5.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, ] [[package]] @@ -272,23 +270,22 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217, upload-time = "2024-08-24T23:15:36.449Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209 }, + { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209, upload-time = "2024-08-24T23:15:35.317Z" }, ] [[package]] name = "attrs" version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] [[package]] name = "aymurai" -version = "2.0.0a7.dev37" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -415,9 +412,9 @@ mlops = [ name = "babel" version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, ] [[package]] @@ -428,9 +425,9 @@ dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] [[package]] @@ -444,9 +441,9 @@ dependencies = [ { name = "lxml" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924 } +sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924, upload-time = "2023-08-15T22:34:59.047Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419 }, + { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419, upload-time = "2023-08-15T22:34:56.022Z" }, ] [[package]] @@ -456,9 +453,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533, upload-time = "2025-10-27T17:57:39.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437, upload-time = "2025-10-27T17:57:37.538Z" }, ] [package.optional-dependencies] @@ -470,9 +467,9 @@ css = [ name = "blinker" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, ] [[package]] @@ -484,9 +481,9 @@ dependencies = [ { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/df/17828670134e56ffca8cf8b017477f16d1a9df7ecfc3870d02aa6d4d2e20/boto3-1.42.23.tar.gz", hash = "sha256:f681a8d43b46b3d8acf0be4f3894eb85e40e75945431d0dfe0542edda7025512", size = 112845 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/df/17828670134e56ffca8cf8b017477f16d1a9df7ecfc3870d02aa6d4d2e20/boto3-1.42.23.tar.gz", hash = "sha256:f681a8d43b46b3d8acf0be4f3894eb85e40e75945431d0dfe0542edda7025512", size = 112845, upload-time = "2026-01-06T20:28:51.006Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/8b/8e028dbf6c4bf36defe899882a00c11a8d22fcb7348e88058900c13b72a0/boto3-1.42.23-py3-none-any.whl", hash = "sha256:2ed797bdb394b08550f6269babf0a31bbeb853684bb2cb67116620df0ed632dc", size = 140574 }, + { url = "https://files.pythonhosted.org/packages/98/8b/8e028dbf6c4bf36defe899882a00c11a8d22fcb7348e88058900c13b72a0/boto3-1.42.23-py3-none-any.whl", hash = "sha256:2ed797bdb394b08550f6269babf0a31bbeb853684bb2cb67116620df0ed632dc", size = 140574, upload-time = "2026-01-06T20:28:48.992Z" }, ] [[package]] @@ -498,27 +495,27 @@ dependencies = [ { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/2c/db33716f86b67c514f895c60694a25cd7428d2137b574b59d09d626b0e2e/botocore-1.42.23.tar.gz", hash = "sha256:453ce449bd1021acd67e75c814aae1b132b1ab3ee0ecff248de863bf19e58be8", size = 14878387 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/2c/db33716f86b67c514f895c60694a25cd7428d2137b574b59d09d626b0e2e/botocore-1.42.23.tar.gz", hash = "sha256:453ce449bd1021acd67e75c814aae1b132b1ab3ee0ecff248de863bf19e58be8", size = 14878387, upload-time = "2026-01-06T20:28:40.151Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/e6/114d66f81fc4b77a8c1e45e6fcf0021018d1f4eefca0905b3da8023c9a47/botocore-1.42.23-py3-none-any.whl", hash = "sha256:d5042e0252b81f25ca1152fff9ed25463bab2438fbc4530ba53d5390d00ca1b1", size = 14551561 }, + { url = "https://files.pythonhosted.org/packages/8d/e6/114d66f81fc4b77a8c1e45e6fcf0021018d1f4eefca0905b3da8023c9a47/botocore-1.42.23-py3-none-any.whl", hash = "sha256:d5042e0252b81f25ca1152fff9ed25463bab2438fbc4530ba53d5390d00ca1b1", size = 14551561, upload-time = "2026-01-06T20:28:36.418Z" }, ] [[package]] name = "cachetools" version = "6.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731, upload-time = "2025-12-15T18:24:53.744Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551 }, + { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551, upload-time = "2025-12-15T18:24:52.332Z" }, ] [[package]] name = "certifi" version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900 }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, ] [[package]] @@ -528,54 +525,54 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283 }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504 }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811 }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402 }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217 }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079 }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475 }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829 }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211 }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036 }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184 }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790 }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, ] [[package]] name = "cfgv" version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 }, - { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 }, - { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 }, - { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 }, - { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 }, - { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 }, - { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 }, - { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 }, - { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 }, - { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 }, - { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 }, - { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 }, - { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 }, - { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 }, - { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 }, - { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] [[package]] @@ -583,29 +580,29 @@ name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Windows' and sys_platform == 'linux') or (platform_system == 'Windows' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] name = "cloudpickle" version = "3.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -613,29 +610,29 @@ name = "colorlog" version = "6.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743 }, + { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, ] [[package]] name = "comm" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, ] [[package]] name = "conllu" version = "4.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768 } +sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768, upload-time = "2023-06-19T12:37:49.632Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098 }, + { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098, upload-time = "2023-06-19T12:37:47.885Z" }, ] [[package]] @@ -645,21 +642,21 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551 }, - { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399 }, - { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061 }, - { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956 }, - { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872 }, - { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027 }, - { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641 }, - { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075 }, - { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534 }, - { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188 }, - { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681 }, - { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101 }, - { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599 }, + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, ] [[package]] @@ -670,49 +667,49 @@ dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004 }, - { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667 }, - { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807 }, - { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615 }, - { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800 }, - { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707 }, - { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541 }, - { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464 }, - { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838 }, - { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596 }, - { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782 }, - { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381 }, - { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988 }, - { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451 }, - { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007 }, - { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248 }, - { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089 }, - { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029 }, - { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222 }, - { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280 }, - { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958 }, - { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714 }, - { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970 }, - { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236 }, - { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642 }, - { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126 }, - { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573 }, - { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695 }, - { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720 }, - { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740 }, - { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163 }, - { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474 }, +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163, upload-time = "2025-10-15T23:18:13.821Z" }, + { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474, upload-time = "2025-10-15T23:18:15.477Z" }, ] [[package]] name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] [[package]] @@ -724,9 +721,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/54/f2393af72974d8e70bfd44b52f17b917190b4934bab66508f4afec9e9285/databricks_sdk-0.77.0.tar.gz", hash = "sha256:56b6017ae17dc31ee821e49746d2ff627a029127166c702088428527813422bd", size = 827462 } +sdist = { url = "https://files.pythonhosted.org/packages/1e/54/f2393af72974d8e70bfd44b52f17b917190b4934bab66508f4afec9e9285/databricks_sdk-0.77.0.tar.gz", hash = "sha256:56b6017ae17dc31ee821e49746d2ff627a029127166c702088428527813422bd", size = 827462, upload-time = "2026-01-06T13:19:09.432Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/aa/d9186684dbcd338cf51e93621bcf53efa843fe647c0f29f549ebddbf2870/databricks_sdk-0.77.0-py3-none-any.whl", hash = "sha256:42e3211b7dbd53b81a795981149ee08d5b025442a73e464264569edc8c46ee0a", size = 779180 }, + { url = "https://files.pythonhosted.org/packages/03/aa/d9186684dbcd338cf51e93621bcf53efa843fe647c0f29f549ebddbf2870/databricks_sdk-0.77.0-py3-none-any.whl", hash = "sha256:42e3211b7dbd53b81a795981149ee08d5b025442a73e464264569edc8c46ee0a", size = 779180, upload-time = "2026-01-06T13:19:07.757Z" }, ] [[package]] @@ -749,9 +746,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/54/9359803da96bc65439a28fbb014dc2c90b7d4d8034a93b72362b0d40191f/datasets-4.4.2.tar.gz", hash = "sha256:9de16e415c4ba4713eac0493f7c7dc74f3aa21599297f00cc6ddab409cb7b24b", size = 586474 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/54/9359803da96bc65439a28fbb014dc2c90b7d4d8034a93b72362b0d40191f/datasets-4.4.2.tar.gz", hash = "sha256:9de16e415c4ba4713eac0493f7c7dc74f3aa21599297f00cc6ddab409cb7b24b", size = 586474, upload-time = "2025-12-19T15:03:09.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/b5/fefa518c809de7bced5cddb7c21c010da66fa2ae494bda96844a280cc6ce/datasets-4.4.2-py3-none-any.whl", hash = "sha256:6f5ef3417504d9cd663c71c1b90b9a494ff4c2076a2cd6a6e40ceee6ad95befc", size = 512268 }, + { url = "https://files.pythonhosted.org/packages/7b/b5/fefa518c809de7bced5cddb7c21c010da66fa2ae494bda96844a280cc6ce/datasets-4.4.2-py3-none-any.whl", hash = "sha256:6f5ef3417504d9cd663c71c1b90b9a494ff4c2076a2cd6a6e40ceee6ad95befc", size = 512268, upload-time = "2025-12-19T15:03:07.087Z" }, ] [[package]] @@ -763,31 +760,31 @@ source = { git = "https://github.com/jedzill4/datetime_matcher#0e5793e8d1e3653f7 name = "debugpy" version = "1.8.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/75/9e12d4d42349b817cd545b89247696c67917aab907012ae5b64bbfea3199/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb", size = 1644590 } +sdist = { url = "https://files.pythonhosted.org/packages/73/75/9e12d4d42349b817cd545b89247696c67917aab907012ae5b64bbfea3199/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb", size = 1644590, upload-time = "2025-12-15T21:53:28.044Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/98/d57054371887f37d3c959a7a8dc3c76b763acb65f5e78d849d7db7cadc5b/debugpy-1.8.19-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fce6da15d73be5935b4438435c53adb512326a3e11e4f90793ea87cd9f018254", size = 2098493 }, - { url = "https://files.pythonhosted.org/packages/ee/dd/c517b9aa3500157a30e4f4c4f5149f880026bd039d2b940acd2383a85d8e/debugpy-1.8.19-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:e24b1652a1df1ab04d81e7ead446a91c226de704ff5dde6bd0a0dbaab07aa3f2", size = 3087875 }, - { url = "https://files.pythonhosted.org/packages/d8/57/3d5a5b0da9b63445253107ead151eff29190c6ad7440c68d1a59d56613aa/debugpy-1.8.19-cp310-cp310-win32.whl", hash = "sha256:327cb28c3ad9e17bc925efc7f7018195fd4787c2fe4b7af1eec11f1d19bdec62", size = 5239378 }, - { url = "https://files.pythonhosted.org/packages/a6/36/7f9053c4c549160c87ae7e43800138f2695578c8b65947114c97250983b6/debugpy-1.8.19-cp310-cp310-win_amd64.whl", hash = "sha256:b7dd275cf2c99e53adb9654f5ae015f70415bbe2bacbe24cfee30d54b6aa03c5", size = 5271129 }, - { url = "https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38", size = 5292321 }, + { url = "https://files.pythonhosted.org/packages/bf/98/d57054371887f37d3c959a7a8dc3c76b763acb65f5e78d849d7db7cadc5b/debugpy-1.8.19-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fce6da15d73be5935b4438435c53adb512326a3e11e4f90793ea87cd9f018254", size = 2098493, upload-time = "2025-12-15T21:53:30.149Z" }, + { url = "https://files.pythonhosted.org/packages/ee/dd/c517b9aa3500157a30e4f4c4f5149f880026bd039d2b940acd2383a85d8e/debugpy-1.8.19-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:e24b1652a1df1ab04d81e7ead446a91c226de704ff5dde6bd0a0dbaab07aa3f2", size = 3087875, upload-time = "2025-12-15T21:53:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/d8/57/3d5a5b0da9b63445253107ead151eff29190c6ad7440c68d1a59d56613aa/debugpy-1.8.19-cp310-cp310-win32.whl", hash = "sha256:327cb28c3ad9e17bc925efc7f7018195fd4787c2fe4b7af1eec11f1d19bdec62", size = 5239378, upload-time = "2025-12-15T21:53:32.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/36/7f9053c4c549160c87ae7e43800138f2695578c8b65947114c97250983b6/debugpy-1.8.19-cp310-cp310-win_amd64.whl", hash = "sha256:b7dd275cf2c99e53adb9654f5ae015f70415bbe2bacbe24cfee30d54b6aa03c5", size = 5271129, upload-time = "2025-12-15T21:53:35.085Z" }, + { url = "https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38", size = 5292321, upload-time = "2025-12-15T21:54:16.024Z" }, ] [[package]] name = "decorator" version = "5.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] [[package]] name = "defusedxml" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, ] [[package]] @@ -797,54 +794,54 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] [[package]] name = "dill" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976 } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668 }, + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, ] [[package]] name = "diskcache" version = "5.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] [[package]] name = "distro" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] [[package]] name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] [[package]] @@ -852,28 +849,28 @@ name = "docker" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywin32", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "requests" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834 } +sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774 }, + { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, ] [[package]] name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } [[package]] name = "docx2txt" version = "0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613, upload-time = "2025-03-24T20:59:25.21Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025 }, + { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025, upload-time = "2025-03-24T20:59:24.394Z" }, ] [[package]] @@ -900,18 +897,18 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/bd/339e1d949aa24e50b6edca8574cf8afbff005a1dfadc2d5b82a0003fa7da/dspy-3.1.0.tar.gz", hash = "sha256:3c1660cb687411f064509cd7ac47ed0231761f50ed92823cbbb59b3af2885702", size = 242611 } +sdist = { url = "https://files.pythonhosted.org/packages/d9/bd/339e1d949aa24e50b6edca8574cf8afbff005a1dfadc2d5b82a0003fa7da/dspy-3.1.0.tar.gz", hash = "sha256:3c1660cb687411f064509cd7ac47ed0231761f50ed92823cbbb59b3af2885702", size = 242611, upload-time = "2026-01-06T18:50:18.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/34/64e900657cad3c4df8a417fccbc4370ca851864edbd0f468210f1b57d084/dspy-3.1.0-py3-none-any.whl", hash = "sha256:b9f4d42cad9ac32b13fdf085dd3246c8ee8339c759cb291c5e7b7f9f5fb5dd72", size = 291317 }, + { url = "https://files.pythonhosted.org/packages/ca/34/64e900657cad3c4df8a417fccbc4370ca851864edbd0f468210f1b57d084/dspy-3.1.0-py3-none-any.whl", hash = "sha256:b9f4d42cad9ac32b13fdf085dd3246c8ee8339c759cb291c5e7b7f9f5fb5dd72", size = 291317, upload-time = "2026-01-06T18:50:17.559Z" }, ] [[package]] name = "einops" version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805, upload-time = "2025-02-09T03:17:00.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359, upload-time = "2025-02-09T03:17:01.998Z" }, ] [[package]] @@ -922,9 +919,9 @@ dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] [[package]] @@ -934,18 +931,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] [[package]] name = "executing" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] [[package]] @@ -955,9 +952,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644, upload-time = "2023-06-27T15:24:28.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039 }, + { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039, upload-time = "2023-06-27T15:24:24.869Z" }, ] [[package]] @@ -970,9 +967,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682 } +sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094 }, + { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, ] [package.optional-dependencies] @@ -997,9 +994,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/ca/d90fb3bfbcbd6e56c77afd9d114dd6ce8955d8bb90094399d1c70e659e40/fastapi_cli-0.0.20.tar.gz", hash = "sha256:d17c2634f7b96b6b560bc16b0035ed047d523c912011395f49f00a421692bc3a", size = 19786 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/ca/d90fb3bfbcbd6e56c77afd9d114dd6ce8955d8bb90094399d1c70e659e40/fastapi_cli-0.0.20.tar.gz", hash = "sha256:d17c2634f7b96b6b560bc16b0035ed047d523c912011395f49f00a421692bc3a", size = 19786, upload-time = "2025-12-22T17:13:33.794Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/89/5c4eef60524d0fd704eb0706885b82cd5623a43396b94e4a5b17d3a3f516/fastapi_cli-0.0.20-py3-none-any.whl", hash = "sha256:e58b6a0038c0b1532b7a0af690656093dee666201b6b19d3c87175b358e9f783", size = 12390 }, + { url = "https://files.pythonhosted.org/packages/08/89/5c4eef60524d0fd704eb0706885b82cd5623a43396b94e4a5b17d3a3f516/fastapi_cli-0.0.20-py3-none-any.whl", hash = "sha256:e58b6a0038c0b1532b7a0af690656093dee666201b6b19d3c87175b358e9f783", size = 12390, upload-time = "2025-12-22T17:13:31.708Z" }, ] [package.optional-dependencies] @@ -1022,89 +1019,89 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/5d/3b33438de35521fab4968b232caa9a4bd568a5078f2b2dfb7bb8a4528603/fastapi_cloud_cli-0.8.0.tar.gz", hash = "sha256:cf07c502528bfd9e6b184776659f05d9212811d76bbec9fbb6bf34bed4c7456f", size = 30257 } +sdist = { url = "https://files.pythonhosted.org/packages/51/5d/3b33438de35521fab4968b232caa9a4bd568a5078f2b2dfb7bb8a4528603/fastapi_cloud_cli-0.8.0.tar.gz", hash = "sha256:cf07c502528bfd9e6b184776659f05d9212811d76bbec9fbb6bf34bed4c7456f", size = 30257, upload-time = "2025-12-23T12:08:33.904Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/8e/abb95ef59e91bb5adaa2d18fbf9ea70fd524010bb03f406a2dd2a4775ef9/fastapi_cloud_cli-0.8.0-py3-none-any.whl", hash = "sha256:e9f40bee671d985fd25d7a5409b56d4f103777bf8a0c6d746ea5fbf97a8186d9", size = 22306 }, + { url = "https://files.pythonhosted.org/packages/dd/8e/abb95ef59e91bb5adaa2d18fbf9ea70fd524010bb03f406a2dd2a4775ef9/fastapi_cloud_cli-0.8.0-py3-none-any.whl", hash = "sha256:e9f40bee671d985fd25d7a5409b56d4f103777bf8a0c6d746ea5fbf97a8186d9", size = 22306, upload-time = "2025-12-23T12:08:32.68Z" }, ] [[package]] name = "fastar" version = "0.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536 }, - { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235 }, - { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386 }, - { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955 }, - { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987 }, - { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900 }, - { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523 }, - { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268 }, - { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286 }, - { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921 }, - { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302 }, - { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141 }, - { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544 }, - { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701 }, - { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544 }, - { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020 }, - { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735 }, - { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779 }, - { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755 }, - { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732 }, - { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571 }, - { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440 }, - { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424 }, - { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675 }, - { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098 }, - { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397 }, +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536, upload-time = "2025-11-26T02:34:35.236Z" }, + { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235, upload-time = "2025-11-26T02:34:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386, upload-time = "2025-11-26T02:33:47.613Z" }, + { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955, upload-time = "2025-11-26T02:32:44.279Z" }, + { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987, upload-time = "2025-11-26T02:32:59.701Z" }, + { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900, upload-time = "2025-11-26T02:33:16.059Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523, upload-time = "2025-11-26T02:33:30.897Z" }, + { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268, upload-time = "2025-11-26T02:34:04.003Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286, upload-time = "2025-11-26T02:34:50.279Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921, upload-time = "2025-11-26T02:35:07.292Z" }, + { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302, upload-time = "2025-11-26T02:35:24.995Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141, upload-time = "2025-11-26T02:35:42.449Z" }, + { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544, upload-time = "2025-11-26T02:36:23.801Z" }, + { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701, upload-time = "2025-11-26T02:36:09.625Z" }, + { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544, upload-time = "2025-11-26T02:34:46.195Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020, upload-time = "2025-11-26T02:34:31.085Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735, upload-time = "2025-11-26T02:34:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779, upload-time = "2025-11-26T02:32:55.109Z" }, + { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755, upload-time = "2025-11-26T02:33:11.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732, upload-time = "2025-11-26T02:33:27.122Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571, upload-time = "2025-11-26T02:33:42.986Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440, upload-time = "2025-11-26T02:34:15.439Z" }, + { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424, upload-time = "2025-11-26T02:35:02.742Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675, upload-time = "2025-11-26T02:35:20.252Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098, upload-time = "2025-11-26T02:35:37.643Z" }, + { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397, upload-time = "2025-11-26T02:35:56.215Z" }, ] [[package]] name = "fastjsonschema" version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, ] [[package]] name = "fastuuid" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760 }, - { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748 }, - { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537 }, - { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994 }, - { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003 }, - { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583 }, - { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955 }, - { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763 }, - { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613 }, - { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045 }, - { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122 }, + { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760, upload-time = "2025-10-19T22:25:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748, upload-time = "2025-10-19T22:41:52.873Z" }, + { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537, upload-time = "2025-10-19T22:33:55.603Z" }, + { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994, upload-time = "2025-10-19T22:26:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003, upload-time = "2025-10-19T22:23:45.415Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583, upload-time = "2025-10-19T22:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955, upload-time = "2025-10-19T22:36:25.196Z" }, + { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763, upload-time = "2025-10-19T22:24:28.451Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613, upload-time = "2025-10-19T22:25:06.827Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045, upload-time = "2025-10-19T22:28:32.732Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122, upload-time = "2025-10-19T22:23:15.59Z" }, ] [[package]] name = "filelock" version = "3.20.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510 } +sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510, upload-time = "2026-01-02T15:33:32.582Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697 }, + { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" }, ] [[package]] name = "filetype" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020 } +sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970 }, + { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, ] [[package]] @@ -1114,9 +1111,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "termcolor" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720, upload-time = "2025-08-16T20:20:24.175Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945 }, + { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945, upload-time = "2025-08-16T20:20:22.87Z" }, ] [[package]] @@ -1150,9 +1147,9 @@ dependencies = [ { name = "transformers", extra = ["sentencepiece"] }, { name = "wikipedia-api" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607 } +sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607, upload-time = "2025-02-05T14:45:44.322Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, + { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604, upload-time = "2025-02-05T14:45:41.788Z" }, ] [[package]] @@ -1167,9 +1164,9 @@ dependencies = [ { name = "markupsafe" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/6d/cfe3c0fcc5e477df242b98bfe186a4c34357b4847e87ecaef04507332dab/flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", size = 720160 } +sdist = { url = "https://files.pythonhosted.org/packages/dc/6d/cfe3c0fcc5e477df242b98bfe186a4c34357b4847e87ecaef04507332dab/flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", size = 720160, upload-time = "2025-08-19T21:03:21.205Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308 }, + { url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308, upload-time = "2025-08-19T21:03:19.499Z" }, ] [[package]] @@ -1180,69 +1177,69 @@ dependencies = [ { name = "flask" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472 } +sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472, upload-time = "2025-12-12T20:31:42.861Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257 }, + { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257, upload-time = "2025-12-12T20:31:41.3Z" }, ] [[package]] name = "fonttools" version = "4.61.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799 }, - { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032 }, - { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863 }, - { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076 }, - { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623 }, - { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327 }, - { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180 }, - { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654 }, - { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996 }, + { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799, upload-time = "2025-12-12T17:29:27.5Z" }, + { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032, upload-time = "2025-12-12T17:29:30.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863, upload-time = "2025-12-12T17:29:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076, upload-time = "2025-12-12T17:29:34.907Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623, upload-time = "2025-12-12T17:29:37.33Z" }, + { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327, upload-time = "2025-12-12T17:29:39.781Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180, upload-time = "2025-12-12T17:29:42.217Z" }, + { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654, upload-time = "2025-12-12T17:29:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" }, ] [[package]] name = "fqdn" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, ] [[package]] name = "frozenlist" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230 }, - { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621 }, - { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889 }, - { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464 }, - { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649 }, - { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188 }, - { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748 }, - { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351 }, - { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767 }, - { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887 }, - { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785 }, - { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312 }, - { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650 }, - { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659 }, - { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837 }, - { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989 }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] [[package]] name = "fsspec" version = "2025.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285 } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966 }, + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, ] [package.optional-dependencies] @@ -1257,9 +1254,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, ] [[package]] @@ -1272,18 +1269,18 @@ dependencies = [ { name = "requests", extra = ["socks"] }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/6a/37e6b70c5bda3161e40265861e63b64a86bfc6ca6a8f1c35328a675c84fd/gdown-5.2.0.tar.gz", hash = "sha256:2145165062d85520a3cd98b356c9ed522c5e7984d408535409fd46f94defc787", size = 284647 } +sdist = { url = "https://files.pythonhosted.org/packages/09/6a/37e6b70c5bda3161e40265861e63b64a86bfc6ca6a8f1c35328a675c84fd/gdown-5.2.0.tar.gz", hash = "sha256:2145165062d85520a3cd98b356c9ed522c5e7984d408535409fd46f94defc787", size = 284647, upload-time = "2024-05-12T06:45:12.725Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/70/e07c381e6488a77094f04c85c9caf1c8008cdc30778f7019bc52e5285ef0/gdown-5.2.0-py3-none-any.whl", hash = "sha256:33083832d82b1101bdd0e9df3edd0fbc0e1c5f14c9d8c38d2a35bf1683b526d6", size = 18235 }, + { url = "https://files.pythonhosted.org/packages/54/70/e07c381e6488a77094f04c85c9caf1c8008cdc30778f7019bc52e5285ef0/gdown-5.2.0-py3-none-any.whl", hash = "sha256:33083832d82b1101bdd0e9df3edd0fbc0e1c5f14c9d8c38d2a35bf1683b526d6", size = 18235, upload-time = "2024-05-12T06:45:10.017Z" }, ] [[package]] name = "gepa" version = "0.0.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/be/9a4c31c65c4910e73bd4a316df99347681bce4f2ef6d10877cc1ed8d57da/gepa-0.0.22.tar.gz", hash = "sha256:0b13a644efd2af52186e456eaad2ae28fcf88c6f796a9c999910c587863d4315", size = 116337 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/be/9a4c31c65c4910e73bd4a316df99347681bce4f2ef6d10877cc1ed8d57da/gepa-0.0.22.tar.gz", hash = "sha256:0b13a644efd2af52186e456eaad2ae28fcf88c6f796a9c999910c587863d4315", size = 116337, upload-time = "2025-11-10T21:39:27.044Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/0e/d41272b28f2163f5bf840b508cbaf4a0bc01eaeaa6e5d447558d1050eb3b/gepa-0.0.22-py3-none-any.whl", hash = "sha256:ff57ee0442399a5cc62d01411935476bc80cfa8f1c318bed82e3db4c2c834665", size = 119666 }, + { url = "https://files.pythonhosted.org/packages/b3/0e/d41272b28f2163f5bf840b508cbaf4a0bc01eaeaa6e5d447558d1050eb3b/gepa-0.0.22-py3-none-any.whl", hash = "sha256:ff57ee0442399a5cc62d01411935476bc80cfa8f1c318bed82e3db4c2c834665", size = 119666, upload-time = "2025-11-10T21:39:26.028Z" }, ] [[package]] @@ -1293,9 +1290,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smmap" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, ] [[package]] @@ -1305,9 +1302,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371 } +sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620 }, + { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, ] [[package]] @@ -1321,9 +1318,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759 } +sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759, upload-time = "2025-10-28T21:34:51.529Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706 }, + { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706, upload-time = "2025-10-28T21:34:50.151Z" }, ] [[package]] @@ -1334,9 +1331,9 @@ dependencies = [ { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/3c/ec64b9a275ca22fa1cd3b6e77fefcf837b0732c890aa32d2bd21313d9b33/google_auth-2.47.0.tar.gz", hash = "sha256:833229070a9dfee1a353ae9877dcd2dec069a8281a4e72e72f77d4a70ff945da", size = 323719 } +sdist = { url = "https://files.pythonhosted.org/packages/60/3c/ec64b9a275ca22fa1cd3b6e77fefcf837b0732c890aa32d2bd21313d9b33/google_auth-2.47.0.tar.gz", hash = "sha256:833229070a9dfee1a353ae9877dcd2dec069a8281a4e72e72f77d4a70ff945da", size = 323719, upload-time = "2026-01-06T21:55:31.045Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/18/79e9008530b79527e0d5f79e7eef08d3b179b7f851cfd3a2f27822fbdfa9/google_auth-2.47.0-py3-none-any.whl", hash = "sha256:c516d68336bfde7cf0da26aab674a36fedcf04b37ac4edd59c597178760c3498", size = 234867 }, + { url = "https://files.pythonhosted.org/packages/db/18/79e9008530b79527e0d5f79e7eef08d3b179b7f851cfd3a2f27822fbdfa9/google_auth-2.47.0-py3-none-any.whl", hash = "sha256:c516d68336bfde7cf0da26aab674a36fedcf04b37ac4edd59c597178760c3498", size = 234867, upload-time = "2026-01-06T21:55:28.6Z" }, ] [package.optional-dependencies] @@ -1352,9 +1349,9 @@ dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027, upload-time = "2025-10-29T23:17:39.513Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469 }, + { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469, upload-time = "2025-10-29T23:17:38.548Z" }, ] [[package]] @@ -1369,22 +1366,22 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d2/8e/fab2de1a0ab7fdbd452eaae5a9a5c933d0911c26b04efa0c76ddfd921259/google_cloud_storage-3.7.0.tar.gz", hash = "sha256:9ce59c65f4d6e372effcecc0456680a8d73cef4f2dc9212a0704799cb3d69237", size = 17258914 } +sdist = { url = "https://files.pythonhosted.org/packages/d2/8e/fab2de1a0ab7fdbd452eaae5a9a5c933d0911c26b04efa0c76ddfd921259/google_cloud_storage-3.7.0.tar.gz", hash = "sha256:9ce59c65f4d6e372effcecc0456680a8d73cef4f2dc9212a0704799cb3d69237", size = 17258914, upload-time = "2025-12-09T18:24:48.97Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/80/6e5c7c83cea15ed4dfc4843b9df9db0716bc551ac938f7b5dd18a72bd5e4/google_cloud_storage-3.7.0-py3-none-any.whl", hash = "sha256:469bc9540936e02f8a4bfd1619e9dca1e42dec48f95e4204d783b36476a15093", size = 303364 }, + { url = "https://files.pythonhosted.org/packages/2d/80/6e5c7c83cea15ed4dfc4843b9df9db0716bc551ac938f7b5dd18a72bd5e4/google_cloud_storage-3.7.0-py3-none-any.whl", hash = "sha256:469bc9540936e02f8a4bfd1619e9dca1e42dec48f95e4204d783b36476a15093", size = 303364, upload-time = "2025-12-09T18:24:47.343Z" }, ] [[package]] name = "google-crc32c" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192 } +sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192, upload-time = "2025-12-16T00:35:25.142Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/ac/6f7bc93886a823ab545948c2dd48143027b2355ad1944c7cf852b338dc91/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff", size = 31296 }, - { url = "https://files.pythonhosted.org/packages/f7/97/a5accde175dee985311d949cfcb1249dcbb290f5ec83c994ea733311948f/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288", size = 30870 }, - { url = "https://files.pythonhosted.org/packages/3d/63/bec827e70b7a0d4094e7476f863c0dbd6b5f0f1f91d9c9b32b76dcdfeb4e/google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d", size = 33214 }, - { url = "https://files.pythonhosted.org/packages/63/bc/11b70614df04c289128d782efc084b9035ef8466b3d0a8757c1b6f5cf7ac/google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092", size = 33589 }, - { url = "https://files.pythonhosted.org/packages/3e/00/a08a4bc24f1261cc5b0f47312d8aebfbe4b53c2e6307f1b595605eed246b/google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733", size = 34437 }, + { url = "https://files.pythonhosted.org/packages/95/ac/6f7bc93886a823ab545948c2dd48143027b2355ad1944c7cf852b338dc91/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff", size = 31296, upload-time = "2025-12-16T00:19:07.261Z" }, + { url = "https://files.pythonhosted.org/packages/f7/97/a5accde175dee985311d949cfcb1249dcbb290f5ec83c994ea733311948f/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288", size = 30870, upload-time = "2025-12-16T00:29:17.669Z" }, + { url = "https://files.pythonhosted.org/packages/3d/63/bec827e70b7a0d4094e7476f863c0dbd6b5f0f1f91d9c9b32b76dcdfeb4e/google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d", size = 33214, upload-time = "2025-12-16T00:40:19.618Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/11b70614df04c289128d782efc084b9035ef8466b3d0a8757c1b6f5cf7ac/google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092", size = 33589, upload-time = "2025-12-16T00:40:20.7Z" }, + { url = "https://files.pythonhosted.org/packages/3e/00/a08a4bc24f1261cc5b0f47312d8aebfbe4b53c2e6307f1b595605eed246b/google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733", size = 34437, upload-time = "2025-12-16T00:35:19.437Z" }, ] [[package]] @@ -1403,9 +1400,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/ad/d3ac5a102135bd3f1e4b1475ca65d2bd4bcc22eb2e9348ac40fe3fadb1d6/google_genai-1.56.0.tar.gz", hash = "sha256:0491af33c375f099777ae207d9621f044e27091fafad4c50e617eba32165e82f", size = 340451 } +sdist = { url = "https://files.pythonhosted.org/packages/70/ad/d3ac5a102135bd3f1e4b1475ca65d2bd4bcc22eb2e9348ac40fe3fadb1d6/google_genai-1.56.0.tar.gz", hash = "sha256:0491af33c375f099777ae207d9621f044e27091fafad4c50e617eba32165e82f", size = 340451, upload-time = "2025-12-17T12:35:05.412Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/93/94bc7a89ef4e7ed3666add55cd859d1483a22737251df659bf1aa46e9405/google_genai-1.56.0-py3-none-any.whl", hash = "sha256:9e6b11e0c105ead229368cb5849a480e4d0185519f8d9f538d61ecfcf193b052", size = 426563 }, + { url = "https://files.pythonhosted.org/packages/84/93/94bc7a89ef4e7ed3666add55cd859d1483a22737251df659bf1aa46e9405/google_genai-1.56.0-py3-none-any.whl", hash = "sha256:9e6b11e0c105ead229368cb5849a480e4d0185519f8d9f538d61ecfcf193b052", size = 426563, upload-time = "2025-12-17T12:35:03.717Z" }, ] [[package]] @@ -1415,9 +1412,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-crc32c" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265 } +sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265, upload-time = "2025-11-17T15:38:06.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340 }, + { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340, upload-time = "2025-11-17T15:38:05.594Z" }, ] [[package]] @@ -1427,9 +1424,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515 }, + { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, ] [[package]] @@ -1442,18 +1439,18 @@ dependencies = [ { name = "python-dateutil" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739, upload-time = "2024-11-09T20:44:25.757Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894 }, + { url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894, upload-time = "2024-11-09T20:44:23.851Z" }, ] [[package]] name = "graphql-core" version = "3.2.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ac/9b/037a640a2983b09aed4a823f9cf1729e6d780b0671f854efa4727a7affbe/graphql_core-3.2.7.tar.gz", hash = "sha256:27b6904bdd3b43f2a0556dad5d579bdfdeab1f38e8e8788e555bdcb586a6f62c", size = 513484 } +sdist = { url = "https://files.pythonhosted.org/packages/ac/9b/037a640a2983b09aed4a823f9cf1729e6d780b0671f854efa4727a7affbe/graphql_core-3.2.7.tar.gz", hash = "sha256:27b6904bdd3b43f2a0556dad5d579bdfdeab1f38e8e8788e555bdcb586a6f62c", size = 513484, upload-time = "2025-11-01T22:30:40.436Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/14/933037032608787fb92e365883ad6a741c235e0ff992865ec5d904a38f1e/graphql_core-3.2.7-py3-none-any.whl", hash = "sha256:17fc8f3ca4a42913d8e24d9ac9f08deddf0a0b2483076575757f6c412ead2ec0", size = 207262 }, + { url = "https://files.pythonhosted.org/packages/0a/14/933037032608787fb92e365883ad6a741c235e0ff992865ec5d904a38f1e/graphql_core-3.2.7-py3-none-any.whl", hash = "sha256:17fc8f3ca4a42913d8e24d9ac9f08deddf0a0b2483076575757f6c412ead2ec0", size = 207262, upload-time = "2025-11-01T22:30:38.912Z" }, ] [[package]] @@ -1463,25 +1460,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "graphql-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/13/98fbf8d67552f102488ffc16c6f559ce71ea15f6294728d33928ab5ff14d/graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c", size = 50027 } +sdist = { url = "https://files.pythonhosted.org/packages/d1/13/98fbf8d67552f102488ffc16c6f559ce71ea15f6294728d33928ab5ff14d/graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c", size = 50027, upload-time = "2022-04-16T11:03:45.447Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/16/a4cf06adbc711bd364a73ce043b0b08d8fa5aae3df11b6ee4248bcdad2e0/graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5", size = 16940 }, + { url = "https://files.pythonhosted.org/packages/74/16/a4cf06adbc711bd364a73ce043b0b08d8fa5aae3df11b6ee4248bcdad2e0/graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5", size = 16940, upload-time = "2022-04-16T11:03:43.895Z" }, ] [[package]] name = "greenlet" version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658 }, - { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810 }, - { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248 }, - { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403 }, - { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910 }, - { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206 }, - { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359 }, - { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740 }, + { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658, upload-time = "2025-12-04T14:23:37.494Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810, upload-time = "2025-12-04T14:50:04.154Z" }, + { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248, upload-time = "2025-12-04T14:57:39.35Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403, upload-time = "2025-12-04T15:07:10.831Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910, upload-time = "2025-12-04T14:25:59.705Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206, upload-time = "2025-12-04T15:04:21.027Z" }, + { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359, upload-time = "2025-12-04T14:27:26.548Z" }, + { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740, upload-time = "2025-12-04T14:47:52.773Z" }, ] [[package]] @@ -1491,33 +1488,33 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 } +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 }, + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, ] [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "hf-xet" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099 }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178 }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214 }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054 }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812 }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920 }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, ] [[package]] @@ -1528,24 +1525,24 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] name = "httptools" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531 }, - { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408 }, - { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889 }, - { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460 }, - { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267 }, - { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429 }, - { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173 }, + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" }, ] [[package]] @@ -1558,18 +1555,18 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] name = "huey" version = "2.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/29/3428d52eb8e85025e264a291641a9f9d6407cc1e51d1b630f6ac5815999a/huey-2.6.0.tar.gz", hash = "sha256:8d11f8688999d65266af1425b831f6e3773e99415027177b8734b0ffd5e251f6", size = 221068 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/29/3428d52eb8e85025e264a291641a9f9d6407cc1e51d1b630f6ac5815999a/huey-2.6.0.tar.gz", hash = "sha256:8d11f8688999d65266af1425b831f6e3773e99415027177b8734b0ffd5e251f6", size = 221068, upload-time = "2026-01-06T03:01:02.055Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/34/fae9ac8f1c3a552fd3f7ff652b94c78d219dedc5fce0c0a4232457760a00/huey-2.6.0-py3-none-any.whl", hash = "sha256:1b9df9d370b49c6d5721ba8a01ac9a787cf86b3bdc584e4679de27b920395c3f", size = 76951 }, + { url = "https://files.pythonhosted.org/packages/1a/34/fae9ac8f1c3a552fd3f7ff652b94c78d219dedc5fce0c0a4232457760a00/huey-2.6.0-py3-none-any.whl", hash = "sha256:1b9df9d370b49c6d5721ba8a01ac9a787cf86b3bdc584e4679de27b920395c3f", size = 76951, upload-time = "2026-01-06T03:01:00.808Z" }, ] [[package]] @@ -1586,27 +1583,27 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358 } +sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358, upload-time = "2025-10-23T12:12:01.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094 }, + { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094, upload-time = "2025-10-23T12:11:59.557Z" }, ] [[package]] name = "identify" version = "2.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183 }, + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] @@ -1616,9 +1613,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, ] [[package]] @@ -1628,9 +1625,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/53/c3/b2afa612aa0373f3e6bb190e6de35f293b307d1537f109e3e25dbfcdf212/intervaltree-3.2.1.tar.gz", hash = "sha256:f3f7e8baeb7dd75b9f7a6d33cf3ec10025984a8e66e3016d537e52130c73cfe2", size = 1231531 } +sdist = { url = "https://files.pythonhosted.org/packages/53/c3/b2afa612aa0373f3e6bb190e6de35f293b307d1537f109e3e25dbfcdf212/intervaltree-3.2.1.tar.gz", hash = "sha256:f3f7e8baeb7dd75b9f7a6d33cf3ec10025984a8e66e3016d537e52130c73cfe2", size = 1231531, upload-time = "2025-12-24T04:25:06.773Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/7f/8a80a1c7c2ed05822b5a2b312d2995f30c533641f8198366ba2e26a7bb03/intervaltree-3.2.1-py2.py3-none-any.whl", hash = "sha256:a8a8381bbd35d48ceebee932c77ffc988492d22fb1d27d0ba1d74a7694eb8f0b", size = 25929 }, + { url = "https://files.pythonhosted.org/packages/83/7f/8a80a1c7c2ed05822b5a2b312d2995f30c533641f8198366ba2e26a7bb03/intervaltree-3.2.1-py2.py3-none-any.whl", hash = "sha256:a8a8381bbd35d48ceebee932c77ffc988492d22fb1d27d0ba1d74a7694eb8f0b", size = 25929, upload-time = "2025-12-24T04:25:05.298Z" }, ] [[package]] @@ -1638,7 +1635,7 @@ name = "ipykernel" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "(platform_machine != 'aarch64' and platform_system == 'Darwin') or (platform_system == 'Darwin' and sys_platform != 'linux')" }, + { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, { name = "ipython" }, @@ -1652,9 +1649,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579, upload-time = "2025-10-27T09:46:39.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968 }, + { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968, upload-time = "2025-10-27T09:46:37.805Z" }, ] [[package]] @@ -1662,7 +1659,7 @@ name = "ipython" version = "8.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "decorator" }, { name = "exceptiongroup" }, { name = "jedi" }, @@ -1674,9 +1671,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813 }, + { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, ] [[package]] @@ -1690,9 +1687,9 @@ dependencies = [ { name = "traitlets" }, { name = "widgetsnbextension" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, ] [[package]] @@ -1702,18 +1699,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "arrow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, ] [[package]] name = "itsdangerous" version = "2.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 }, + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, ] [[package]] @@ -1723,9 +1720,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parso" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, ] [[package]] @@ -1735,37 +1732,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "jiter" version = "0.12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652 }, - { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829 }, - { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568 }, - { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052 }, - { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585 }, - { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541 }, - { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423 }, - { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958 }, - { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084 }, - { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054 }, - { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368 }, - { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847 }, - { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144 }, - { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877 }, - { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419 }, - { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212 }, - { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974 }, - { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233 }, - { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537 }, - { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110 }, +sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652, upload-time = "2025-11-09T20:46:41.021Z" }, + { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829, upload-time = "2025-11-09T20:46:43.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568, upload-time = "2025-11-09T20:46:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052, upload-time = "2025-11-09T20:46:46.818Z" }, + { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585, upload-time = "2025-11-09T20:46:48.319Z" }, + { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541, upload-time = "2025-11-09T20:46:49.643Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423, upload-time = "2025-11-09T20:46:51.731Z" }, + { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958, upload-time = "2025-11-09T20:46:53.432Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084, upload-time = "2025-11-09T20:46:54.848Z" }, + { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054, upload-time = "2025-11-09T20:46:56.487Z" }, + { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368, upload-time = "2025-11-09T20:46:58.638Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847, upload-time = "2025-11-09T20:47:00.295Z" }, ] [[package]] @@ -1776,45 +1765,45 @@ dependencies = [ { name = "click" }, { name = "rapidfuzz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537 } +sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537, upload-time = "2024-11-01T16:18:57.337Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990, upload-time = "2024-11-01T16:18:55.928Z" }, ] [[package]] name = "jmespath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 } +sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 }, + { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, ] [[package]] name = "joblib" version = "1.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] [[package]] name = "json-repair" version = "0.55.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/74/0f39677fa7c0127129c3f1a37c94d05c30a968ba3047200e54dea375b09a/json_repair-0.55.0.tar.gz", hash = "sha256:9fafb47d92582ef4bdd3520656bdb0fcb37b46cf6aa99c1926b7895abc0a3a4b", size = 38828 } +sdist = { url = "https://files.pythonhosted.org/packages/96/74/0f39677fa7c0127129c3f1a37c94d05c30a968ba3047200e54dea375b09a/json_repair-0.55.0.tar.gz", hash = "sha256:9fafb47d92582ef4bdd3520656bdb0fcb37b46cf6aa99c1926b7895abc0a3a4b", size = 38828, upload-time = "2026-01-01T20:29:02.684Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/00/d7d6b6b3257f9b1f997a558b6f7087b8af7c0a2f525f4fbd864c267a88ab/json_repair-0.55.0-py3-none-any.whl", hash = "sha256:bcf4880f5e6ad21a0f70ab034e3d1d398c2ae9698dc5717d7015afbac77b8ed7", size = 29570 }, + { url = "https://files.pythonhosted.org/packages/bc/00/d7d6b6b3257f9b1f997a558b6f7087b8af7c0a2f525f4fbd864c267a88ab/json_repair-0.55.0-py3-none-any.whl", hash = "sha256:bcf4880f5e6ad21a0f70ab034e3d1d398c2ae9698dc5717d7015afbac77b8ed7", size = 29570, upload-time = "2026-01-01T20:29:01.042Z" }, ] [[package]] name = "json5" version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441 } +sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441, upload-time = "2026-01-01T19:42:14.99Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163 }, + { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163, upload-time = "2026-01-01T19:42:13.962Z" }, ] [[package]] @@ -1824,18 +1813,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359 } +sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359, upload-time = "2023-09-01T12:34:44.187Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701 }, + { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701, upload-time = "2023-09-01T12:34:42.563Z" }, ] [[package]] name = "jsonpointer" version = "3.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, ] [[package]] @@ -1848,9 +1837,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] [package.optional-dependencies] @@ -1873,9 +1862,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] [[package]] @@ -1890,9 +1879,9 @@ dependencies = [ { name = "nbconvert" }, { name = "notebook" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, ] [[package]] @@ -1906,9 +1895,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/27/d10de45e8ad4ce872372c4a3a37b7b35b6b064f6f023a5c14ffcced4d59d/jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d", size = 344691 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/27/d10de45e8ad4ce872372c4a3a37b7b35b6b064f6f023a5c14ffcced4d59d/jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d", size = 344691, upload-time = "2025-12-09T18:37:01.953Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0", size = 106215 }, + { url = "https://files.pythonhosted.org/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0", size = 106215, upload-time = "2025-12-09T18:37:00.024Z" }, ] [[package]] @@ -1925,9 +1914,9 @@ dependencies = [ { name = "pyzmq" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, ] [[package]] @@ -1938,9 +1927,9 @@ dependencies = [ { name = "platformdirs" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, ] [[package]] @@ -1957,9 +1946,9 @@ dependencies = [ { name = "rfc3986-validator" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430 }, + { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, ] [[package]] @@ -1969,9 +1958,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823, upload-time = "2025-08-27T17:47:34.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687 }, + { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687, upload-time = "2025-08-27T17:47:33.15Z" }, ] [[package]] @@ -1991,7 +1980,7 @@ dependencies = [ { name = "overrides" }, { name = "packaging" }, { name = "prometheus-client" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "pyzmq" }, { name = "send2trash" }, { name = "terminado" }, @@ -1999,9 +1988,9 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949, upload-time = "2025-08-21T14:42:54.042Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221 }, + { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221, upload-time = "2025-08-21T14:42:52.034Z" }, ] [[package]] @@ -2009,12 +1998,12 @@ name = "jupyter-server-terminals" version = "0.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "terminado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430, upload-time = "2024-03-12T14:37:03.049Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656 }, + { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656, upload-time = "2024-03-12T14:37:00.708Z" }, ] [[package]] @@ -2037,18 +2026,18 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/21/413d142686a4e8f4268d985becbdb4daf060524726248e73be4773786987/jupyterlab-4.5.1.tar.gz", hash = "sha256:09da1ddfbd9eec18b5101dbb8515612aa1e47443321fb99503725a88e93d20d9", size = 23992251 } +sdist = { url = "https://files.pythonhosted.org/packages/09/21/413d142686a4e8f4268d985becbdb4daf060524726248e73be4773786987/jupyterlab-4.5.1.tar.gz", hash = "sha256:09da1ddfbd9eec18b5101dbb8515612aa1e47443321fb99503725a88e93d20d9", size = 23992251, upload-time = "2025-12-15T16:58:59.361Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-py3-none-any.whl", hash = "sha256:31b059de96de0754ff1f2ce6279774b6aab8c34d7082e9752db58207c99bd514", size = 12384821 }, + { url = "https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-py3-none-any.whl", hash = "sha256:31b059de96de0754ff1f2ce6279774b6aab8c34d7082e9752db58207c99bd514", size = 12384821, upload-time = "2025-12-15T16:58:55.563Z" }, ] [[package]] name = "jupyterlab-pygments" version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, ] [[package]] @@ -2064,44 +2053,44 @@ dependencies = [ { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, ] [[package]] name = "jupyterlab-widgets" version = "3.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, ] [[package]] name = "kiwisolver" version = "1.4.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159 }, - { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578 }, - { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312 }, - { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458 }, - { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640 }, - { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074 }, - { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036 }, - { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310 }, - { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943 }, - { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488 }, - { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787 }, - { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730 }, - { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036 }, - { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183 }, - { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675 }, - { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277 }, - { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994 }, - { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744 }, +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" }, + { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" }, + { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" }, + { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" }, + { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" }, + { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" }, + { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" }, + { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" }, + { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" }, + { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" }, + { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" }, + { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" }, ] [[package]] @@ -2111,7 +2100,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474, upload-time = "2021-05-07T07:54:13.562Z" } [[package]] name = "langextract" @@ -2135,9 +2124,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029 } +sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029, upload-time = "2025-11-14T22:21:30.511Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592 }, + { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592, upload-time = "2025-11-14T22:21:29.044Z" }, ] [package.optional-dependencies] @@ -2149,9 +2138,9 @@ openai = [ name = "lark" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, ] [[package]] @@ -2161,9 +2150,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fire" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292, upload-time = "2022-09-06T16:09:06.607Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594 }, + { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594, upload-time = "2022-09-06T16:09:04.658Z" }, ] [[package]] @@ -2184,39 +2173,39 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976, upload-time = "2025-11-16T00:03:51.812Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975 }, + { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975, upload-time = "2025-11-16T00:03:49.182Z" }, ] [[package]] name = "lxml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589 }, - { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671 }, - { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961 }, - { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087 }, - { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620 }, - { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664 }, - { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397 }, - { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178 }, - { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148 }, - { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035 }, - { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111 }, - { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662 }, - { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973 }, - { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953 }, - { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695 }, - { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051 }, - { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264 }, - { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435 }, - { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913 }, - { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357 }, - { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295 }, - { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913 }, +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589, upload-time = "2025-09-22T04:00:10.51Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671, upload-time = "2025-09-22T04:00:15.411Z" }, + { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961, upload-time = "2025-09-22T04:00:17.619Z" }, + { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087, upload-time = "2025-09-22T04:00:19.868Z" }, + { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620, upload-time = "2025-09-22T04:00:21.877Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664, upload-time = "2025-09-22T04:00:23.714Z" }, + { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397, upload-time = "2025-09-22T04:00:25.544Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178, upload-time = "2025-09-22T04:00:27.602Z" }, + { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148, upload-time = "2025-09-22T04:00:29.323Z" }, + { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035, upload-time = "2025-09-22T04:00:31.061Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111, upload-time = "2025-09-22T04:00:33.11Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662, upload-time = "2025-09-22T04:00:35.237Z" }, + { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973, upload-time = "2025-09-22T04:00:37.086Z" }, + { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953, upload-time = "2025-09-22T04:00:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695, upload-time = "2025-09-22T04:00:41.402Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051, upload-time = "2025-09-22T04:00:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264, upload-time = "2025-09-22T04:04:32.892Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435, upload-time = "2025-09-22T04:04:34.907Z" }, + { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913, upload-time = "2025-09-22T04:04:37.205Z" }, + { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357, upload-time = "2025-09-22T04:04:39.322Z" }, + { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295, upload-time = "2025-09-22T04:04:41.647Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913, upload-time = "2025-09-22T04:04:43.602Z" }, ] [[package]] @@ -2226,9 +2215,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, ] [[package]] @@ -2238,18 +2227,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "markdown2" version = "2.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652 } +sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652, upload-time = "2025-07-27T16:16:24.307Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954 }, + { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954, upload-time = "2025-07-27T16:16:23.026Z" }, ] [[package]] @@ -2260,9 +2249,9 @@ dependencies = [ { name = "beautifulsoup4" }, { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816, upload-time = "2025-11-16T19:21:18.565Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724 }, + { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724, upload-time = "2025-11-16T19:21:17.622Z" }, ] [[package]] @@ -2292,28 +2281,28 @@ dependencies = [ { name = "tqdm" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302 } +sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302, upload-time = "2025-09-30T15:44:59.591Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914 }, + { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914, upload-time = "2025-09-30T15:44:58.441Z" }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631 }, - { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057 }, - { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050 }, - { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681 }, - { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705 }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524 }, - { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282 }, - { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745 }, - { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571 }, - { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056 }, - { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932 }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, ] [[package]] @@ -2331,17 +2320,17 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828 }, - { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050 }, - { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452 }, - { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928 }, - { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377 }, - { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127 }, - { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252 }, - { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693 }, - { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205 }, + { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, + { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, + { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, ] [[package]] @@ -2351,18 +2340,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516 }, + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] @@ -2372,9 +2361,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467, upload-time = "2025-12-23T11:36:34.994Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598 }, + { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" }, ] [[package]] @@ -2385,9 +2374,9 @@ dependencies = [ { name = "absl-py" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356 } +sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356, upload-time = "2025-04-17T08:25:02.247Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707 }, + { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707, upload-time = "2025-04-17T08:24:59.038Z" }, ] [[package]] @@ -2401,7 +2390,7 @@ dependencies = [ { name = "flask" }, { name = "flask-cors" }, { name = "graphene" }, - { name = "gunicorn", marker = "platform_system != 'Windows'" }, + { name = "gunicorn", marker = "sys_platform != 'win32'" }, { name = "huey" }, { name = "matplotlib" }, { name = "mlflow-skinny" }, @@ -2412,11 +2401,11 @@ dependencies = [ { name = "scikit-learn" }, { name = "scipy" }, { name = "sqlalchemy" }, - { name = "waitress", marker = "(platform_machine != 'aarch64' and platform_system == 'Windows' and sys_platform == 'linux') or (platform_system == 'Windows' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "waitress", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/f8/10ccb111ed53732dfceae0369073023f96acd6b00f92fb3c24473938702d/mlflow-3.8.1.tar.gz", hash = "sha256:0823377bedff4d530b0d560bf394daf9f7e9fbba53453add04eadad34de962cc", size = 8550037 } +sdist = { url = "https://files.pythonhosted.org/packages/ad/f8/10ccb111ed53732dfceae0369073023f96acd6b00f92fb3c24473938702d/mlflow-3.8.1.tar.gz", hash = "sha256:0823377bedff4d530b0d560bf394daf9f7e9fbba53453add04eadad34de962cc", size = 8550037, upload-time = "2025-12-26T16:46:49.199Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/d5/a20b87c6cd99395fee04d6034686512305530c71ceaabe3a151eeaa25ed7/mlflow-3.8.1-py3-none-any.whl", hash = "sha256:42f26b52438fdb615588e150407c6516d0f64d417436dfc75599c525a464f210", size = 9062281 }, + { url = "https://files.pythonhosted.org/packages/d5/d5/a20b87c6cd99395fee04d6034686512305530c71ceaabe3a151eeaa25ed7/mlflow-3.8.1-py3-none-any.whl", hash = "sha256:42f26b52438fdb615588e150407c6516d0f64d417436dfc75599c525a464f210", size = 9062281, upload-time = "2025-12-26T16:46:46.528Z" }, ] [[package]] @@ -2444,9 +2433,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/00/18486d9072739e63471c1e441e78cdb6a10c641312d98f6699715406451e/mlflow_skinny-3.8.1.tar.gz", hash = "sha256:0c0aade08187030a4653e267bcd63de2f12cbfebf4c6737832cba45d6fb3594d", size = 2082226 } +sdist = { url = "https://files.pythonhosted.org/packages/7e/00/18486d9072739e63471c1e441e78cdb6a10c641312d98f6699715406451e/mlflow_skinny-3.8.1.tar.gz", hash = "sha256:0c0aade08187030a4653e267bcd63de2f12cbfebf4c6737832cba45d6fb3594d", size = 2082226, upload-time = "2025-12-26T16:30:11.171Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/24/42e52320636fcbabeaf50704f9269a328acc995e1b8a44df6fea33130a0a/mlflow_skinny-3.8.1-py3-none-any.whl", hash = "sha256:3a6ee27f5ac1e67c1d565fa0e12c070b27129b03e669dcaf88ff841176429142", size = 2506002 }, + { url = "https://files.pythonhosted.org/packages/8d/24/42e52320636fcbabeaf50704f9269a328acc995e1b8a44df6fea33130a0a/mlflow_skinny-3.8.1-py3-none-any.whl", hash = "sha256:3a6ee27f5ac1e67c1d565fa0e12c070b27129b03e669dcaf88ff841176429142", size = 2506002, upload-time = "2025-12-26T16:30:09.357Z" }, ] [[package]] @@ -2463,18 +2452,18 @@ dependencies = [ { name = "protobuf" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/e5/9cdca5a91afd71e15943f3dbe00e788bd36479637e9b2e0625224027ff01/mlflow_tracing-3.8.1.tar.gz", hash = "sha256:c032ba715994a4580323f3045fa2700a6323033d87e564bbcbda37e6ab993071", size = 1130700 } +sdist = { url = "https://files.pythonhosted.org/packages/52/e5/9cdca5a91afd71e15943f3dbe00e788bd36479637e9b2e0625224027ff01/mlflow_tracing-3.8.1.tar.gz", hash = "sha256:c032ba715994a4580323f3045fa2700a6323033d87e564bbcbda37e6ab993071", size = 1130700, upload-time = "2025-12-26T16:31:39.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/e3/ddbe3e2d1219fa9235559ab88ebf98e1e4f48c62672f0dfb8f1eb07276dc/mlflow_tracing-3.8.1-py3-none-any.whl", hash = "sha256:12d9b5b7177b4152979d003e0d967b280c4252758639aebdfd672734283b17bf", size = 1359007 }, + { url = "https://files.pythonhosted.org/packages/fd/e3/ddbe3e2d1219fa9235559ab88ebf98e1e4f48c62672f0dfb8f1eb07276dc/mlflow_tracing-3.8.1-py3-none-any.whl", hash = "sha256:12d9b5b7177b4152979d003e0d967b280c4252758639aebdfd672734283b17bf", size = 1359007, upload-time = "2025-12-26T16:31:37.514Z" }, ] [[package]] name = "more-itertools" version = "10.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667 }, + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, ] [[package]] @@ -2485,18 +2474,18 @@ dependencies = [ { name = "jinja2" }, { name = "matplotlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433, upload-time = "2025-11-05T18:12:24.183Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, + { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051, upload-time = "2025-11-05T18:12:22.527Z" }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] [[package]] @@ -2506,27 +2495,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153 }, - { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993 }, - { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607 }, - { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847 }, - { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616 }, - { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333 }, - { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239 }, - { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618 }, - { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655 }, - { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245 }, - { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523 }, - { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129 }, - { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999 }, - { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711 }, - { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504 }, - { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422 }, - { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050 }, - { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153 }, - { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317 }, +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153, upload-time = "2025-10-06T14:48:26.409Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993, upload-time = "2025-10-06T14:48:28.4Z" }, + { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607, upload-time = "2025-10-06T14:48:29.581Z" }, + { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847, upload-time = "2025-10-06T14:48:32.107Z" }, + { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616, upload-time = "2025-10-06T14:48:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333, upload-time = "2025-10-06T14:48:35.9Z" }, + { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239, upload-time = "2025-10-06T14:48:37.302Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618, upload-time = "2025-10-06T14:48:38.963Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655, upload-time = "2025-10-06T14:48:40.312Z" }, + { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245, upload-time = "2025-10-06T14:48:41.848Z" }, + { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523, upload-time = "2025-10-06T14:48:43.749Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129, upload-time = "2025-10-06T14:48:45.225Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999, upload-time = "2025-10-06T14:48:46.703Z" }, + { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711, upload-time = "2025-10-06T14:48:48.146Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504, upload-time = "2025-10-06T14:48:49.447Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422, upload-time = "2025-10-06T14:48:50.789Z" }, + { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050, upload-time = "2025-10-06T14:48:51.938Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153, upload-time = "2025-10-06T14:48:53.146Z" }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, ] [[package]] @@ -2536,23 +2525,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503 } +sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503, upload-time = "2025-04-17T03:11:27.742Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083 }, - { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128 }, - { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132 }, - { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948 }, - { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636 }, - { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478 }, + { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083, upload-time = "2025-04-17T03:11:04.223Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128, upload-time = "2025-04-17T03:11:06.045Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132, upload-time = "2025-04-17T03:11:07.533Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948, upload-time = "2025-04-17T03:11:20.223Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636, upload-time = "2025-04-17T03:11:24.936Z" }, + { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z" }, ] [[package]] name = "narwhals" version = "2.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/6d/b57c64e5038a8cf071bce391bb11551657a74558877ac961e7fa905ece27/narwhals-2.15.0.tar.gz", hash = "sha256:a9585975b99d95084268445a1fdd881311fa26ef1caa18020d959d5b2ff9a965", size = 603479 } +sdist = { url = "https://files.pythonhosted.org/packages/47/6d/b57c64e5038a8cf071bce391bb11551657a74558877ac961e7fa905ece27/narwhals-2.15.0.tar.gz", hash = "sha256:a9585975b99d95084268445a1fdd881311fa26ef1caa18020d959d5b2ff9a965", size = 603479, upload-time = "2026-01-06T08:10:13.27Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.whl", hash = "sha256:cbfe21ca19d260d9fd67f995ec75c44592d1f106933b03ddd375df7ac841f9d6", size = 432856 }, + { url = "https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.whl", hash = "sha256:cbfe21ca19d260d9fd67f995ec75c44592d1f106933b03ddd375df7ac841f9d6", size = 432856, upload-time = "2026-01-06T08:10:11.511Z" }, ] [[package]] @@ -2565,9 +2554,9 @@ dependencies = [ { name = "nbformat" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554 } +sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554, upload-time = "2025-12-23T07:45:46.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465 }, + { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465, upload-time = "2025-12-23T07:45:44.51Z" }, ] [[package]] @@ -2590,9 +2579,9 @@ dependencies = [ { name = "pygments" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715, upload-time = "2025-01-28T09:29:14.724Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525 }, + { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525, upload-time = "2025-01-28T09:29:12.551Z" }, ] [[package]] @@ -2605,9 +2594,9 @@ dependencies = [ { name = "jupyter-core" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, ] [[package]] @@ -2617,36 +2606,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nbformat" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747 } +sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747, upload-time = "2025-11-16T17:38:55.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122 }, + { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122, upload-time = "2025-11-16T17:38:54.164Z" }, ] [[package]] name = "nest-asyncio" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, ] [[package]] name = "networkx" version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, ] [[package]] name = "nodeenv" version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611 } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438 }, + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] [[package]] @@ -2660,9 +2649,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/a9/882707b0aa639e6d7d3e7df4bfbe07479d832e9a8f02d8471002a4ea6d65/notebook-7.5.1.tar.gz", hash = "sha256:b2fb4cef4d47d08c33aecce1c6c6e84be05436fbd791f88fce8df9fbca088b75", size = 14058696 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/a9/882707b0aa639e6d7d3e7df4bfbe07479d832e9a8f02d8471002a4ea6d65/notebook-7.5.1.tar.gz", hash = "sha256:b2fb4cef4d47d08c33aecce1c6c6e84be05436fbd791f88fce8df9fbca088b75", size = 14058696, upload-time = "2025-12-16T07:38:59.223Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/86/ca516cb58ad2cb2064124d31cf0fd8b012fca64bebeb26da2d2ddf03fc79/notebook-7.5.1-py3-none-any.whl", hash = "sha256:f4e2451c19910c33b88709b84537e11f6368c1cdff1aa0c43db701aea535dd44", size = 14468080 }, + { url = "https://files.pythonhosted.org/packages/d1/86/ca516cb58ad2cb2064124d31cf0fd8b012fca64bebeb26da2d2ddf03fc79/notebook-7.5.1-py3-none-any.whl", hash = "sha256:f4e2451c19910c33b88709b84537e11f6368c1cdff1aa0c43db701aea535dd44", size = 14468080, upload-time = "2025-12-16T07:38:55.644Z" }, ] [[package]] @@ -2672,25 +2661,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, ] [[package]] @@ -2698,8 +2687,7 @@ name = "nvidia-cublas-cu12" version = "12.8.4.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, ] [[package]] @@ -2707,8 +2695,7 @@ name = "nvidia-cuda-cupti-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, ] [[package]] @@ -2716,8 +2703,7 @@ name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, - { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, ] [[package]] @@ -2725,8 +2711,7 @@ name = "nvidia-cuda-runtime-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, ] [[package]] @@ -2737,8 +2722,7 @@ dependencies = [ { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, ] [[package]] @@ -2749,8 +2733,7 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, ] [[package]] @@ -2758,8 +2741,7 @@ name = "nvidia-cufile-cu12" version = "1.13.1.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, - { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, ] [[package]] @@ -2767,8 +2749,7 @@ name = "nvidia-curand-cu12" version = "10.3.9.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, ] [[package]] @@ -2781,8 +2762,7 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, ] [[package]] @@ -2793,8 +2773,7 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, ] [[package]] @@ -2802,8 +2781,7 @@ name = "nvidia-cusparselt-cu12" version = "0.7.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, ] [[package]] @@ -2811,8 +2789,7 @@ name = "nvidia-nccl-cu12" version = "2.27.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, ] [[package]] @@ -2820,8 +2797,7 @@ name = "nvidia-nvjitlink-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, - { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, ] [[package]] @@ -2829,8 +2805,7 @@ name = "nvidia-nvshmem-cu12" version = "3.3.20" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/9d/3dd98852568fb845ec1f7902c90a22b240fe1cbabda411ccedf2fd737b7b/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b0b960da3842212758e4fa4696b94f129090b30e5122fea3c5345916545cff0", size = 124484616 }, - { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, + { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" }, ] [[package]] @@ -2838,8 +2813,7 @@ name = "nvidia-nvtx-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, ] [[package]] @@ -2849,7 +2823,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045 } +sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045, upload-time = "2020-01-18T16:55:48.852Z" } [[package]] name = "ollama" @@ -2859,9 +2833,9 @@ dependencies = [ { name = "httpx" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620, upload-time = "2025-11-13T23:02:17.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354 }, + { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354, upload-time = "2025-11-13T23:02:16.292Z" }, ] [[package]] @@ -2878,9 +2852,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133, upload-time = "2025-09-24T13:00:53.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627 }, + { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627, upload-time = "2025-09-24T13:00:50.754Z" }, ] [[package]] @@ -2890,14 +2864,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, - { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, - { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060 }, - { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856 }, - { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425 }, - { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, + { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload-time = "2025-01-16T13:52:57.015Z" }, + { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload-time = "2025-01-16T13:55:45.731Z" }, + { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload-time = "2025-01-16T13:51:59.625Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload-time = "2025-01-16T13:53:29.654Z" }, + { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload-time = "2025-01-16T13:52:49.048Z" }, + { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload-time = "2025-01-16T13:52:56.418Z" }, ] [[package]] @@ -2908,9 +2882,9 @@ dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767 } +sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356 }, + { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, ] [[package]] @@ -2920,9 +2894,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152 } +sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535 }, + { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, ] [[package]] @@ -2934,9 +2908,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565 }, + { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, ] [[package]] @@ -2947,9 +2921,9 @@ dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935 } +sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982 }, + { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, ] [[package]] @@ -2965,48 +2939,48 @@ dependencies = [ { name = "sqlalchemy" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444, upload-time = "2025-11-10T05:14:30.151Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708 }, + { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708, upload-time = "2025-11-10T05:14:28.6Z" }, ] [[package]] name = "orjson" version = "3.11.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/b8/333fdb27840f3bf04022d21b654a35f58e15407183aeb16f3b41aa053446/orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5", size = 5972347 } +sdist = { url = "https://files.pythonhosted.org/packages/04/b8/333fdb27840f3bf04022d21b654a35f58e15407183aeb16f3b41aa053446/orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5", size = 5972347, upload-time = "2025-12-06T15:55:39.458Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/19/b22cf9dad4db20c8737041046054cbd4f38bb5a2d0e4bb60487832ce3d76/orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1", size = 245719 }, - { url = "https://files.pythonhosted.org/packages/03/2e/b136dd6bf30ef5143fbe76a4c142828b55ccc618be490201e9073ad954a1/orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870", size = 132467 }, - { url = "https://files.pythonhosted.org/packages/ae/fc/ae99bfc1e1887d20a0268f0e2686eb5b13d0ea7bbe01de2b566febcd2130/orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09", size = 130702 }, - { url = "https://files.pythonhosted.org/packages/6e/43/ef7912144097765997170aca59249725c3ab8ef6079f93f9d708dd058df5/orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd", size = 135907 }, - { url = "https://files.pythonhosted.org/packages/3f/da/24d50e2d7f4092ddd4d784e37a3fa41f22ce8ed97abc9edd222901a96e74/orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac", size = 139935 }, - { url = "https://files.pythonhosted.org/packages/02/4a/b4cb6fcbfff5b95a3a019a8648255a0fac9b221fbf6b6e72be8df2361feb/orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e", size = 137541 }, - { url = "https://files.pythonhosted.org/packages/a5/99/a11bd129f18c2377c27b2846a9d9be04acec981f770d711ba0aaea563984/orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f", size = 139031 }, - { url = "https://files.pythonhosted.org/packages/64/29/d7b77d7911574733a036bb3e8ad7053ceb2b7d6ea42208b9dbc55b23b9ed/orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18", size = 141622 }, - { url = "https://files.pythonhosted.org/packages/93/41/332db96c1de76b2feda4f453e91c27202cd092835936ce2b70828212f726/orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a", size = 413800 }, - { url = "https://files.pythonhosted.org/packages/76/e1/5a0d148dd1f89ad2f9651df67835b209ab7fcb1118658cf353425d7563e9/orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7", size = 151198 }, - { url = "https://files.pythonhosted.org/packages/0d/96/8db67430d317a01ae5cf7971914f6775affdcfe99f5bff9ef3da32492ecc/orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401", size = 141984 }, - { url = "https://files.pythonhosted.org/packages/71/49/40d21e1aa1ac569e521069228bb29c9b5a350344ccf922a0227d93c2ed44/orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8", size = 135272 }, - { url = "https://files.pythonhosted.org/packages/c4/7e/d0e31e78be0c100e08be64f48d2850b23bcb4d4c70d114f4e43b39f6895a/orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167", size = 133360 }, + { url = "https://files.pythonhosted.org/packages/79/19/b22cf9dad4db20c8737041046054cbd4f38bb5a2d0e4bb60487832ce3d76/orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1", size = 245719, upload-time = "2025-12-06T15:53:43.877Z" }, + { url = "https://files.pythonhosted.org/packages/03/2e/b136dd6bf30ef5143fbe76a4c142828b55ccc618be490201e9073ad954a1/orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870", size = 132467, upload-time = "2025-12-06T15:53:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/ae/fc/ae99bfc1e1887d20a0268f0e2686eb5b13d0ea7bbe01de2b566febcd2130/orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09", size = 130702, upload-time = "2025-12-06T15:53:46.659Z" }, + { url = "https://files.pythonhosted.org/packages/6e/43/ef7912144097765997170aca59249725c3ab8ef6079f93f9d708dd058df5/orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd", size = 135907, upload-time = "2025-12-06T15:53:48.487Z" }, + { url = "https://files.pythonhosted.org/packages/3f/da/24d50e2d7f4092ddd4d784e37a3fa41f22ce8ed97abc9edd222901a96e74/orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac", size = 139935, upload-time = "2025-12-06T15:53:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/02/4a/b4cb6fcbfff5b95a3a019a8648255a0fac9b221fbf6b6e72be8df2361feb/orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e", size = 137541, upload-time = "2025-12-06T15:53:51.226Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/a11bd129f18c2377c27b2846a9d9be04acec981f770d711ba0aaea563984/orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f", size = 139031, upload-time = "2025-12-06T15:53:52.309Z" }, + { url = "https://files.pythonhosted.org/packages/64/29/d7b77d7911574733a036bb3e8ad7053ceb2b7d6ea42208b9dbc55b23b9ed/orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18", size = 141622, upload-time = "2025-12-06T15:53:53.606Z" }, + { url = "https://files.pythonhosted.org/packages/93/41/332db96c1de76b2feda4f453e91c27202cd092835936ce2b70828212f726/orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a", size = 413800, upload-time = "2025-12-06T15:53:54.866Z" }, + { url = "https://files.pythonhosted.org/packages/76/e1/5a0d148dd1f89ad2f9651df67835b209ab7fcb1118658cf353425d7563e9/orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7", size = 151198, upload-time = "2025-12-06T15:53:56.383Z" }, + { url = "https://files.pythonhosted.org/packages/0d/96/8db67430d317a01ae5cf7971914f6775affdcfe99f5bff9ef3da32492ecc/orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401", size = 141984, upload-time = "2025-12-06T15:53:57.746Z" }, + { url = "https://files.pythonhosted.org/packages/71/49/40d21e1aa1ac569e521069228bb29c9b5a350344ccf922a0227d93c2ed44/orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8", size = 135272, upload-time = "2025-12-06T15:53:59.769Z" }, + { url = "https://files.pythonhosted.org/packages/c4/7e/d0e31e78be0c100e08be64f48d2850b23bcb4d4c70d114f4e43b39f6895a/orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167", size = 133360, upload-time = "2025-12-06T15:54:01.25Z" }, ] [[package]] name = "overrides" version = "7.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] @@ -3019,33 +2993,33 @@ dependencies = [ { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763 }, - { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217 }, - { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791 }, - { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373 }, - { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444 }, - { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459 }, - { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086 }, + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, ] [[package]] name = "pandocfilters" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, ] [[package]] name = "parso" version = "0.8.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668 }, + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, ] [[package]] @@ -3058,9 +3032,9 @@ dependencies = [ { name = "pydantic-settings" }, { name = "pypdfium2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968, upload-time = "2025-06-11T14:42:09.492Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693 }, + { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693, upload-time = "2025-06-11T14:42:08.157Z" }, ] [[package]] @@ -3070,53 +3044,53 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, ] [[package]] name = "pillow" version = "10.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, - { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, - { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, - { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, - { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, - { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, - { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, - { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, - { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, - { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, - { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, - { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, - { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, - { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, - { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, - { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, - { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, - { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload-time = "2024-07-01T09:45:22.07Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload-time = "2024-07-01T09:45:25.292Z" }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload-time = "2024-07-01T09:45:27.94Z" }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload-time = "2024-07-01T09:45:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload-time = "2024-07-01T09:45:32.868Z" }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload-time = "2024-07-01T09:45:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload-time = "2024-07-01T09:45:37.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload-time = "2024-07-01T09:45:39.89Z" }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload-time = "2024-07-01T09:45:42.771Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload-time = "2024-07-01T09:45:45.176Z" }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload-time = "2024-07-01T09:45:47.274Z" }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload-time = "2024-07-01T09:48:04.815Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload-time = "2024-07-01T09:48:07.206Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload-time = "2024-07-01T09:48:09.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload-time = "2024-07-01T09:48:12.529Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload-time = "2024-07-01T09:48:14.891Z" }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload-time = "2024-07-01T09:48:17.601Z" }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload-time = "2024-07-01T09:48:20.293Z" }, ] [[package]] name = "pip" version = "25.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622 }, + { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" }, ] [[package]] name = "platformdirs" version = "4.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731 }, + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, ] [[package]] @@ -3128,14 +3102,14 @@ dependencies = [ { name = "pyee" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424 }, - { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228 }, - { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424 }, - { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122 }, - { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645 }, - { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837 }, - { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843 }, - { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959 }, + { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424, upload-time = "2025-11-11T18:39:10.175Z" }, + { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228, upload-time = "2025-11-11T18:39:13.915Z" }, + { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424, upload-time = "2025-11-11T18:39:17.093Z" }, + { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122, upload-time = "2025-11-11T18:39:20.619Z" }, + { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645, upload-time = "2025-11-11T18:39:24.005Z" }, + { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837, upload-time = "2025-11-11T18:39:27.174Z" }, + { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843, upload-time = "2025-11-11T18:39:30.851Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959, upload-time = "2025-11-11T18:39:33.998Z" }, ] [[package]] @@ -3146,16 +3120,16 @@ dependencies = [ { name = "narwhals" }, { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624 } +sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624, upload-time = "2025-11-17T18:39:24.523Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174 }, + { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174, upload-time = "2025-11-17T18:39:20.351Z" }, ] [[package]] name = "pptree" version = "3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043 } +sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043, upload-time = "2020-04-15T18:28:53.362Z" } [[package]] name = "pre-commit" @@ -3168,18 +3142,18 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232 } +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437 }, + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] [[package]] name = "prometheus-client" version = "0.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481 } +sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481, upload-time = "2025-09-18T20:47:25.043Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145 }, + { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, ] [[package]] @@ -3189,33 +3163,33 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, ] [[package]] name = "propcache" version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534 }, - { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526 }, - { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263 }, - { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012 }, - { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491 }, - { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319 }, - { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856 }, - { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241 }, - { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552 }, - { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113 }, - { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778 }, - { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047 }, - { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093 }, - { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638 }, - { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229 }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] [[package]] @@ -3225,81 +3199,81 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/01/89/9cbe2f4bba860e149108b683bc2efec21f14d5f7ed6e25562ad86acbc373/proto_plus-1.27.0.tar.gz", hash = "sha256:873af56dd0d7e91836aee871e5799e1c6f1bda86ac9a983e0bb9f0c266a568c4", size = 56158 } +sdist = { url = "https://files.pythonhosted.org/packages/01/89/9cbe2f4bba860e149108b683bc2efec21f14d5f7ed6e25562ad86acbc373/proto_plus-1.27.0.tar.gz", hash = "sha256:873af56dd0d7e91836aee871e5799e1c6f1bda86ac9a983e0bb9f0c266a568c4", size = 56158, upload-time = "2025-12-16T13:46:25.729Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/24/3b7a0818484df9c28172857af32c2397b6d8fcd99d9468bd4684f98ebf0a/proto_plus-1.27.0-py3-none-any.whl", hash = "sha256:1baa7f81cf0f8acb8bc1f6d085008ba4171eaf669629d1b6d1673b21ed1c0a82", size = 50205 }, + { url = "https://files.pythonhosted.org/packages/cd/24/3b7a0818484df9c28172857af32c2397b6d8fcd99d9468bd4684f98ebf0a/proto_plus-1.27.0-py3-none-any.whl", hash = "sha256:1baa7f81cf0f8acb8bc1f6d085008ba4171eaf669629d1b6d1673b21ed1c0a82", size = 50205, upload-time = "2025-12-16T13:46:24.76Z" }, ] [[package]] name = "protobuf" version = "6.33.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296 } +sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603 }, - { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930 }, - { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621 }, - { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460 }, - { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168 }, - { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270 }, - { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501 }, + { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, + { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, + { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, + { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, + { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, ] [[package]] name = "psutil" version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565 } +sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565, upload-time = "2024-10-17T21:31:45.68Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762 }, - { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777 }, - { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259 }, - { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255 }, - { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804 }, - { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386 }, - { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228 }, + { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762, upload-time = "2024-10-17T21:32:05.991Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777, upload-time = "2024-10-17T21:32:07.872Z" }, + { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259, upload-time = "2024-10-17T21:32:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255, upload-time = "2024-10-17T21:32:11.964Z" }, + { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804, upload-time = "2024-10-17T21:32:13.785Z" }, + { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386, upload-time = "2024-10-17T21:32:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228, upload-time = "2024-10-17T21:32:23.88Z" }, ] [[package]] name = "ptyprocess" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, ] [[package]] name = "pure-eval" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] [[package]] name = "pyarrow" version = "22.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151 } +sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151, upload-time = "2025-10-24T12:30:00.762Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968 }, - { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085 }, - { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613 }, - { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059 }, - { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043 }, - { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505 }, - { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641 }, + { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968, upload-time = "2025-10-24T10:03:31.21Z" }, + { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085, upload-time = "2025-10-24T10:03:38.146Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613, upload-time = "2025-10-24T10:03:46.516Z" }, + { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059, upload-time = "2025-10-24T10:03:55.353Z" }, + { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043, upload-time = "2025-10-24T10:04:05.408Z" }, + { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505, upload-time = "2025-10-24T10:04:15.786Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641, upload-time = "2025-10-24T10:04:22.57Z" }, ] [[package]] name = "pyasn1" version = "0.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, ] [[package]] @@ -3309,18 +3283,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, ] [[package]] name = "pycparser" version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] [[package]] @@ -3333,9 +3307,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [package.optional-dependencies] @@ -3350,37 +3324,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298 }, - { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475 }, - { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815 }, - { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567 }, - { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442 }, - { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956 }, - { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253 }, - { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050 }, - { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178 }, - { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833 }, - { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156 }, - { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378 }, - { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622 }, - { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441 }, - { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291 }, - { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632 }, - { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905 }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495 }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388 }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879 }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017 }, - { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351 }, - { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363 }, - { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615 }, - { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369 }, - { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218 }, - { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951 }, - { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428 }, - { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009 }, +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, ] [[package]] @@ -3391,9 +3357,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226 } +sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296 }, + { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, ] [[package]] @@ -3405,9 +3371,9 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184 } +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880 }, + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, ] [[package]] @@ -3418,9 +3384,9 @@ dependencies = [ { name = "jinja2" }, { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, ] [[package]] @@ -3430,33 +3396,33 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250 } +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730 }, + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pymupdf" version = "1.26.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/d6/09b28f027b510838559f7748807192149c419b30cb90e6d5f0cf916dc9dc/pymupdf-1.26.7.tar.gz", hash = "sha256:71add8bdc8eb1aaa207c69a13400693f06ad9b927bea976f5d5ab9df0bb489c3", size = 84327033 } +sdist = { url = "https://files.pythonhosted.org/packages/48/d6/09b28f027b510838559f7748807192149c419b30cb90e6d5f0cf916dc9dc/pymupdf-1.26.7.tar.gz", hash = "sha256:71add8bdc8eb1aaa207c69a13400693f06ad9b927bea976f5d5ab9df0bb489c3", size = 84327033, upload-time = "2025-12-11T21:48:50.694Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/35/cd74cea1787b2247702ef8522186bdef32e9cb30a099e6bb864627ef6045/pymupdf-1.26.7-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:07085718dfdae5ab83b05eb5eb397f863bcc538fe05135318a01ea353e7a1353", size = 23179369 }, - { url = "https://files.pythonhosted.org/packages/72/74/448b6172927c829c6a3fba80078d7b0a016ebbe2c9ee528821f5ea21677a/pymupdf-1.26.7-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:31aa9c8377ea1eea02934b92f4dcf79fb2abba0bf41f8a46d64c3e31546a3c02", size = 22470101 }, - { url = "https://files.pythonhosted.org/packages/65/e7/47af26f3ac76be7ac3dd4d6cc7ee105948a8355d774e5ca39857bf91c11c/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e419b609996434a14a80fa060adec72c434a1cca6a511ec54db9841bc5d51b3c", size = 23502486 }, - { url = "https://files.pythonhosted.org/packages/2a/6b/3de1714d734ff949be1e90a22375d0598d3540b22ae73eb85c2d7d1f36a9/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:69dfc78f206a96e5b3ac22741263ebab945fdf51f0dbe7c5757c3511b23d9d72", size = 24115727 }, - { url = "https://files.pythonhosted.org/packages/62/9b/f86224847949577a523be2207315ae0fd3155b5d909cd66c274d095349a3/pymupdf-1.26.7-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1d5106f46e1ca0d64d46bd51892372a4f82076bdc14a9678d33d630702abca36", size = 24324386 }, - { url = "https://files.pythonhosted.org/packages/85/8e/a117d39092ca645fde8b903f4a941d9aa75b370a67b4f1f435f56393dc5a/pymupdf-1.26.7-cp310-abi3-win32.whl", hash = "sha256:7c9645b6f5452629c747690190350213d3e5bbdb6b2eca227d82702b327f6eee", size = 17203888 }, - { url = "https://files.pythonhosted.org/packages/dd/c3/d0047678146c294469c33bae167c8ace337deafb736b0bf97b9bc481aa65/pymupdf-1.26.7-cp310-abi3-win_amd64.whl", hash = "sha256:425b1befe40d41b72eb0fe211711c7ae334db5eb60307e9dd09066ed060cceba", size = 18405952 }, + { url = "https://files.pythonhosted.org/packages/94/35/cd74cea1787b2247702ef8522186bdef32e9cb30a099e6bb864627ef6045/pymupdf-1.26.7-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:07085718dfdae5ab83b05eb5eb397f863bcc538fe05135318a01ea353e7a1353", size = 23179369, upload-time = "2025-12-11T21:47:21.587Z" }, + { url = "https://files.pythonhosted.org/packages/72/74/448b6172927c829c6a3fba80078d7b0a016ebbe2c9ee528821f5ea21677a/pymupdf-1.26.7-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:31aa9c8377ea1eea02934b92f4dcf79fb2abba0bf41f8a46d64c3e31546a3c02", size = 22470101, upload-time = "2025-12-11T21:47:37.105Z" }, + { url = "https://files.pythonhosted.org/packages/65/e7/47af26f3ac76be7ac3dd4d6cc7ee105948a8355d774e5ca39857bf91c11c/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e419b609996434a14a80fa060adec72c434a1cca6a511ec54db9841bc5d51b3c", size = 23502486, upload-time = "2025-12-12T09:51:25.824Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6b/3de1714d734ff949be1e90a22375d0598d3540b22ae73eb85c2d7d1f36a9/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:69dfc78f206a96e5b3ac22741263ebab945fdf51f0dbe7c5757c3511b23d9d72", size = 24115727, upload-time = "2025-12-11T21:47:51.274Z" }, + { url = "https://files.pythonhosted.org/packages/62/9b/f86224847949577a523be2207315ae0fd3155b5d909cd66c274d095349a3/pymupdf-1.26.7-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1d5106f46e1ca0d64d46bd51892372a4f82076bdc14a9678d33d630702abca36", size = 24324386, upload-time = "2025-12-12T14:58:45.483Z" }, + { url = "https://files.pythonhosted.org/packages/85/8e/a117d39092ca645fde8b903f4a941d9aa75b370a67b4f1f435f56393dc5a/pymupdf-1.26.7-cp310-abi3-win32.whl", hash = "sha256:7c9645b6f5452629c747690190350213d3e5bbdb6b2eca227d82702b327f6eee", size = 17203888, upload-time = "2025-12-12T13:59:57.613Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c3/d0047678146c294469c33bae167c8ace337deafb736b0bf97b9bc481aa65/pymupdf-1.26.7-cp310-abi3-win_amd64.whl", hash = "sha256:425b1befe40d41b72eb0fe211711c7ae334db5eb60307e9dd09066ed060cceba", size = 18405952, upload-time = "2025-12-11T21:48:02.947Z" }, ] [[package]] @@ -3467,56 +3433,56 @@ dependencies = [ { name = "pymupdf" }, { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/ff/d2e856ff5db5fa57ebf13ae04de4da6c203f5fb17507c0dd21ffd0ae4a17/pymupdf4llm-0.2.8.tar.gz", hash = "sha256:c4f9815cff210cef45123e93ae3a6f802e0dc1d1ad4534a4a463c000aaec98fd", size = 66867 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/ff/d2e856ff5db5fa57ebf13ae04de4da6c203f5fb17507c0dd21ffd0ae4a17/pymupdf4llm-0.2.8.tar.gz", hash = "sha256:c4f9815cff210cef45123e93ae3a6f802e0dc1d1ad4534a4a463c000aaec98fd", size = 66867, upload-time = "2026-01-04T16:38:30.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/d5/47e14346b6f57cbc7456cc24ded697b4037b399ff56ed12afccbd202361d/pymupdf4llm-0.2.8-py3-none-any.whl", hash = "sha256:e60587d151223fc9e118661a9c7bb04b04ae928187c201a14d60ac8a42963591", size = 68637 }, + { url = "https://files.pythonhosted.org/packages/58/d5/47e14346b6f57cbc7456cc24ded697b4037b399ff56ed12afccbd202361d/pymupdf4llm-0.2.8-py3-none-any.whl", hash = "sha256:e60587d151223fc9e118661a9c7bb04b04ae928187c201a14d60ac8a42963591", size = 68637, upload-time = "2026-01-04T16:38:32.412Z" }, ] [[package]] name = "pypandoc" version = "1.16.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477, upload-time = "2025-11-13T16:30:29.608Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451 }, + { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451, upload-time = "2025-11-13T16:30:07.66Z" }, ] [[package]] name = "pyparsing" version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/c1/1d9de9aeaa1b89b0186e5fe23294ff6517fce1bc69149185577cd31016b2/pyparsing-3.3.1.tar.gz", hash = "sha256:47fad0f17ac1e2cad3de3b458570fbc9b03560aa029ed5e16ee5554da9a2251c", size = 1550512 } +sdist = { url = "https://files.pythonhosted.org/packages/33/c1/1d9de9aeaa1b89b0186e5fe23294ff6517fce1bc69149185577cd31016b2/pyparsing-3.3.1.tar.gz", hash = "sha256:47fad0f17ac1e2cad3de3b458570fbc9b03560aa029ed5e16ee5554da9a2251c", size = 1550512, upload-time = "2025-12-23T03:14:04.391Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-py3-none-any.whl", hash = "sha256:023b5e7e5520ad96642e2c6db4cb683d3970bd640cdf7115049a6e9c3682df82", size = 121793 }, + { url = "https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-py3-none-any.whl", hash = "sha256:023b5e7e5520ad96642e2c6db4cb683d3970bd640cdf7115049a6e9c3682df82", size = 121793, upload-time = "2025-12-23T03:14:02.103Z" }, ] [[package]] name = "pypdfium2" version = "4.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239, upload-time = "2024-05-09T18:33:17.552Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254 }, - { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624 }, - { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126 }, - { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077 }, - { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431 }, - { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008 }, - { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543 }, - { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911 }, - { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430 }, - { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951 }, - { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098 }, - { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118 }, + { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254, upload-time = "2024-05-09T18:32:48.653Z" }, + { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624, upload-time = "2024-05-09T18:32:51.458Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126, upload-time = "2024-05-09T18:32:53.581Z" }, + { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077, upload-time = "2024-05-09T18:32:55.99Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431, upload-time = "2024-05-09T18:32:57.911Z" }, + { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008, upload-time = "2024-05-09T18:32:59.886Z" }, + { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543, upload-time = "2024-05-09T18:33:02.597Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911, upload-time = "2024-05-09T18:33:05.376Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430, upload-time = "2024-05-09T18:33:08.067Z" }, + { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951, upload-time = "2024-05-09T18:33:10.567Z" }, + { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098, upload-time = "2024-05-09T18:33:13.107Z" }, + { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118, upload-time = "2024-05-09T18:33:15.489Z" }, ] [[package]] name = "pysocks" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725 }, + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, ] [[package]] @@ -3526,9 +3492,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] [[package]] @@ -3539,36 +3505,36 @@ dependencies = [ { name = "lxml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987 }, + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, ] [[package]] name = "python-dotenv" version = "1.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, ] [[package]] name = "python-json-logger" version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683 } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548 }, + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, ] [[package]] name = "python-multipart" version = "0.0.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/78/96/804520d0850c7db98e5ccb70282e29208723f0964e88ffd9d0da2f52ea09/python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92", size = 37196 } +sdist = { url = "https://files.pythonhosted.org/packages/78/96/804520d0850c7db98e5ccb70282e29208723f0964e88ffd9d0da2f52ea09/python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92", size = 37196, upload-time = "2025-12-17T09:24:22.446Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/76/03af049af4dcee5d27442f71b6924f01f3efb5d2bd34f23fcd563f2cc5f5/python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090", size = 24541 }, + { url = "https://files.pythonhosted.org/packages/aa/76/03af049af4dcee5d27442f71b6924f01f3efb5d2bd34f23fcd563f2cc5f5/python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090", size = 24541, upload-time = "2025-12-17T09:24:21.153Z" }, ] [[package]] @@ -3587,9 +3553,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889 } +sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889, upload-time = "2022-11-25T19:33:07.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949 }, + { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949, upload-time = "2022-11-25T19:33:06.093Z" }, ] [[package]] @@ -3600,18 +3566,18 @@ dependencies = [ { name = "numpy" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086 } +sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086, upload-time = "2021-01-09T17:35:49.131Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564 }, + { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564, upload-time = "2021-01-09T17:35:47.543Z" }, ] [[package]] name = "pytz" version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] [[package]] @@ -3619,35 +3585,35 @@ name = "pywin32" version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432 }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103 }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557 }, + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, ] [[package]] name = "pywinpty" version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669, upload-time = "2025-10-03T21:16:29.205Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330 }, + { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330, upload-time = "2025-10-03T21:20:15.656Z" }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227 }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019 }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646 }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793 }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293 }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872 }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828 }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415 }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561 }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, ] [[package]] @@ -3657,52 +3623,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850 }, - { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380 }, - { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421 }, - { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149 }, - { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070 }, - { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441 }, - { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529 }, - { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276 }, - { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208 }, - { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766 }, - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, - { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266 }, - { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206 }, - { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747 }, - { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371 }, - { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862 }, +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850, upload-time = "2025-09-08T23:07:26.274Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380, upload-time = "2025-09-08T23:07:29.78Z" }, + { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421, upload-time = "2025-09-08T23:07:31.263Z" }, + { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149, upload-time = "2025-09-08T23:07:33.17Z" }, + { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070, upload-time = "2025-09-08T23:07:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441, upload-time = "2025-09-08T23:07:37.432Z" }, + { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529, upload-time = "2025-09-08T23:07:39.047Z" }, + { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276, upload-time = "2025-09-08T23:07:40.695Z" }, + { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208, upload-time = "2025-09-08T23:07:42.298Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766, upload-time = "2025-09-08T23:07:43.869Z" }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266, upload-time = "2025-09-08T23:09:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206, upload-time = "2025-09-08T23:09:41.902Z" }, + { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747, upload-time = "2025-09-08T23:09:43.741Z" }, + { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371, upload-time = "2025-09-08T23:09:45.575Z" }, + { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862, upload-time = "2025-09-08T23:09:47.448Z" }, ] [[package]] name = "rapidfuzz" version = "3.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900, upload-time = "2025-11-01T11:54:52.321Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376 }, - { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903 }, - { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655 }, - { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708 }, - { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106 }, - { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048 }, - { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020 }, - { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958 }, - { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043 }, - { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273 }, - { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875 }, + { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376, upload-time = "2025-11-01T11:52:29.175Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903, upload-time = "2025-11-01T11:52:31.239Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655, upload-time = "2025-11-01T11:52:32.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708, upload-time = "2025-11-01T11:52:34.618Z" }, + { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106, upload-time = "2025-11-01T11:52:36.069Z" }, + { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048, upload-time = "2025-11-01T11:52:37.936Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020, upload-time = "2025-11-01T11:52:39.657Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958, upload-time = "2025-11-01T11:52:41.017Z" }, + { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043, upload-time = "2025-11-01T11:52:42.465Z" }, + { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273, upload-time = "2025-11-01T11:52:44.005Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875, upload-time = "2025-11-01T11:52:45.405Z" }, ] [[package]] @@ -3714,33 +3680,33 @@ dependencies = [ { name = "rpds-py" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] [[package]] name = "regex" version = "2024.11.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, - { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, - { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, - { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, - { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, - { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, - { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, - { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, - { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, - { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, - { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, - { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, - { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, - { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, - { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, - { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674, upload-time = "2024-11-06T20:08:57.575Z" }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684, upload-time = "2024-11-06T20:08:59.787Z" }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589, upload-time = "2024-11-06T20:09:01.896Z" }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511, upload-time = "2024-11-06T20:09:04.062Z" }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149, upload-time = "2024-11-06T20:09:06.237Z" }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707, upload-time = "2024-11-06T20:09:07.715Z" }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702, upload-time = "2024-11-06T20:09:10.101Z" }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976, upload-time = "2024-11-06T20:09:11.566Z" }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397, upload-time = "2024-11-06T20:09:13.119Z" }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726, upload-time = "2024-11-06T20:09:14.85Z" }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098, upload-time = "2024-11-06T20:09:16.504Z" }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325, upload-time = "2024-11-06T20:09:18.698Z" }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277, upload-time = "2024-11-06T20:09:21.725Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197, upload-time = "2024-11-06T20:09:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714, upload-time = "2024-11-06T20:09:26.36Z" }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042, upload-time = "2024-11-06T20:09:28.762Z" }, ] [[package]] @@ -3753,9 +3719,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [package.optional-dependencies] @@ -3770,18 +3736,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, ] [[package]] name = "rfc3986-validator" version = "0.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, ] [[package]] @@ -3791,9 +3757,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lark" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, ] [[package]] @@ -3804,9 +3770,9 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393 }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, ] [[package]] @@ -3818,65 +3784,65 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/09/3f9b8d9daaf235195c626f21e03604c05b987404ee3bcacee0c1f67f2a8e/rich_toolkit-0.17.1.tar.gz", hash = "sha256:5af54df8d1dd9c8530e462e1bdcaed625c9b49f5a55b035aa0ba1c17bdb87c9a", size = 187925 } +sdist = { url = "https://files.pythonhosted.org/packages/97/09/3f9b8d9daaf235195c626f21e03604c05b987404ee3bcacee0c1f67f2a8e/rich_toolkit-0.17.1.tar.gz", hash = "sha256:5af54df8d1dd9c8530e462e1bdcaed625c9b49f5a55b035aa0ba1c17bdb87c9a", size = 187925, upload-time = "2025-12-17T10:49:22.583Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/7b/15e55fa8a76d0d41bf34d965af78acdaf80a315907adb30de8b63c272694/rich_toolkit-0.17.1-py3-none-any.whl", hash = "sha256:96d24bb921ecd225ffce7c526a9149e74006410c05e6d405bd74ffd54d5631ed", size = 31412 }, + { url = "https://files.pythonhosted.org/packages/7f/7b/15e55fa8a76d0d41bf34d965af78acdaf80a315907adb30de8b63c272694/rich_toolkit-0.17.1-py3-none-any.whl", hash = "sha256:96d24bb921ecd225ffce7c526a9149e74006410c05e6d405bd74ffd54d5631ed", size = 31412, upload-time = "2025-12-17T10:49:21.793Z" }, ] [[package]] name = "rignore" version = "0.7.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999 }, - { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824 }, - { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121 }, - { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813 }, - { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019 }, - { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822 }, - { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820 }, - { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050 }, - { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164 }, - { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028 }, - { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024 }, - { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531 }, - { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817 }, - { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001 }, - { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462 }, - { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918 }, - { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922 }, - { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987 }, - { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110 }, - { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339 }, - { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680 }, - { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045 }, - { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310 }, - { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998 }, - { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178 }, - { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190 }, +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999, upload-time = "2025-11-05T20:42:38.373Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824, upload-time = "2025-11-05T20:42:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121, upload-time = "2025-11-05T20:40:48.94Z" }, + { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813, upload-time = "2025-11-05T20:41:04.71Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019, upload-time = "2025-11-05T20:41:20.723Z" }, + { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822, upload-time = "2025-11-05T20:41:36.99Z" }, + { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820, upload-time = "2025-11-05T20:42:06.364Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050, upload-time = "2025-11-05T20:41:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164, upload-time = "2025-11-05T21:40:10.368Z" }, + { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028, upload-time = "2025-11-05T21:40:27.977Z" }, + { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024, upload-time = "2025-11-05T21:40:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531, upload-time = "2025-11-05T21:41:02.734Z" }, + { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817, upload-time = "2025-11-05T21:41:47.51Z" }, + { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001, upload-time = "2025-11-05T21:41:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462, upload-time = "2025-11-05T20:42:50.804Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918, upload-time = "2025-11-05T20:42:33.689Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922, upload-time = "2025-11-05T20:41:00.361Z" }, + { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987, upload-time = "2025-11-05T20:41:16.219Z" }, + { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110, upload-time = "2025-11-05T20:41:32.631Z" }, + { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339, upload-time = "2025-11-05T20:41:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680, upload-time = "2025-11-05T20:42:18.061Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045, upload-time = "2025-11-05T20:42:02.315Z" }, + { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310, upload-time = "2025-11-05T21:40:23.184Z" }, + { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998, upload-time = "2025-11-05T21:40:40.603Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178, upload-time = "2025-11-05T21:40:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190, upload-time = "2025-11-05T21:41:16.403Z" }, ] [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490 }, - { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751 }, - { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696 }, - { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136 }, - { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699 }, - { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022 }, - { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522 }, - { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579 }, - { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305 }, - { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503 }, - { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322 }, - { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792 }, - { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901 }, - { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823 }, + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, ] [[package]] @@ -3886,9 +3852,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 } +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 }, + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, ] [[package]] @@ -3898,35 +3864,35 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827 } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830 }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, ] [[package]] name = "safetensors" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, - { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430 }, - { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977 }, - { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890 }, - { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885 }, +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, ] [[package]] @@ -3939,13 +3905,13 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136 } +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221 }, - { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834 }, - { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938 }, - { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818 }, - { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969 }, + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, ] [[package]] @@ -3955,16 +3921,16 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870, upload-time = "2024-06-24T20:35:18.532Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226 }, - { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893 }, - { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258 }, - { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715 }, - { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038 }, - { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959 }, - { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514 }, - { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252 }, + { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226, upload-time = "2024-06-24T20:31:50.451Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893, upload-time = "2024-06-24T20:31:57.337Z" }, + { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258, upload-time = "2024-06-24T20:32:02.711Z" }, + { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715, upload-time = "2024-06-24T20:32:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038, upload-time = "2024-06-24T20:32:17.305Z" }, + { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959, upload-time = "2024-06-24T20:32:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514, upload-time = "2024-06-24T20:32:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252, upload-time = "2024-06-24T20:32:45.06Z" }, ] [[package]] @@ -3976,9 +3942,9 @@ dependencies = [ { name = "numpy" }, { name = "pandas" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, ] [[package]] @@ -3988,18 +3954,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "regex" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244, upload-time = "2021-12-15T21:56:14.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332, upload-time = "2021-12-15T21:56:12.508Z" }, ] [[package]] name = "send2trash" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/6e/421803dec0c0dfbf5a27e66491ebe6643a461e4f90422f00ea4c68ae24aa/send2trash-2.0.0.tar.gz", hash = "sha256:1761421da3f9930bfe51ed7c45343948573383ad4c27e3acebc91be324e7770d", size = 17206 } +sdist = { url = "https://files.pythonhosted.org/packages/62/6e/421803dec0c0dfbf5a27e66491ebe6643a461e4f90422f00ea4c68ae24aa/send2trash-2.0.0.tar.gz", hash = "sha256:1761421da3f9930bfe51ed7c45343948573383ad4c27e3acebc91be324e7770d", size = 17206, upload-time = "2025-12-31T04:12:48.664Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl", hash = "sha256:e70d5ce41dbb890882cc78bc25d137478330b39a391e756fadf82e34da4d85b8", size = 17642 }, + { url = "https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl", hash = "sha256:e70d5ce41dbb890882cc78bc25d137478330b39a391e756fadf82e34da4d85b8", size = 17642, upload-time = "2025-12-31T04:12:45.336Z" }, ] [[package]] @@ -4015,25 +3981,25 @@ dependencies = [ { name = "transformers" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/a1/64e7b111e753307ffb7c5b6d039c52d4a91a47fa32a7f5bc377a49b22402/sentence_transformers-5.2.0.tar.gz", hash = "sha256:acaeb38717de689f3dab45d5e5a02ebe2f75960a4764ea35fea65f58a4d3019f", size = 381004 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/a1/64e7b111e753307ffb7c5b6d039c52d4a91a47fa32a7f5bc377a49b22402/sentence_transformers-5.2.0.tar.gz", hash = "sha256:acaeb38717de689f3dab45d5e5a02ebe2f75960a4764ea35fea65f58a4d3019f", size = 381004, upload-time = "2025-12-11T14:12:31.038Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/d0/3b2897ef6a0c0c801e9fecca26bcc77081648e38e8c772885ebdd8d7d252/sentence_transformers-5.2.0-py3-none-any.whl", hash = "sha256:aa57180f053687d29b08206766ae7db549be5074f61849def7b17bf0b8025ca2", size = 493748 }, + { url = "https://files.pythonhosted.org/packages/40/d0/3b2897ef6a0c0c801e9fecca26bcc77081648e38e8c772885ebdd8d7d252/sentence_transformers-5.2.0-py3-none-any.whl", hash = "sha256:aa57180f053687d29b08206766ae7db549be5074f61849def7b17bf0b8025ca2", size = 493748, upload-time = "2025-12-11T14:12:29.516Z" }, ] [[package]] name = "sentencepiece" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106, upload-time = "2024-02-19T17:06:47.428Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979 }, - { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845 }, - { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472 }, - { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151 }, - { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931 }, - { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537 }, - { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747 }, - { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525 }, + { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979, upload-time = "2024-02-19T17:05:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845, upload-time = "2024-02-19T17:05:37.371Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472, upload-time = "2024-02-19T17:05:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151, upload-time = "2024-02-19T17:05:42.594Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931, upload-time = "2024-02-19T17:05:44.695Z" }, + { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537, upload-time = "2024-02-19T17:05:46.713Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747, upload-time = "2024-02-19T17:05:48.705Z" }, + { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525, upload-time = "2024-02-19T17:05:55.145Z" }, ] [[package]] @@ -4044,72 +4010,72 @@ dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f0/0e9dc590513d5e742d7799e2038df3a05167cba084c6ca4f3cdd75b55164/sentry_sdk-2.48.0.tar.gz", hash = "sha256:5213190977ff7fdff8a58b722fb807f8d5524a80488626ebeda1b5676c0c1473", size = 384828 } +sdist = { url = "https://files.pythonhosted.org/packages/40/f0/0e9dc590513d5e742d7799e2038df3a05167cba084c6ca4f3cdd75b55164/sentry_sdk-2.48.0.tar.gz", hash = "sha256:5213190977ff7fdff8a58b722fb807f8d5524a80488626ebeda1b5676c0c1473", size = 384828, upload-time = "2025-12-16T14:55:41.722Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/19/8d77f9992e5cbfcaa9133c3bf63b4fbbb051248802e1e803fed5c552fbb2/sentry_sdk-2.48.0-py2.py3-none-any.whl", hash = "sha256:6b12ac256769d41825d9b7518444e57fa35b5642df4c7c5e322af4d2c8721172", size = 414555 }, + { url = "https://files.pythonhosted.org/packages/4d/19/8d77f9992e5cbfcaa9133c3bf63b4fbbb051248802e1e803fed5c552fbb2/sentry_sdk-2.48.0-py2.py3-none-any.whl", hash = "sha256:6b12ac256769d41825d9b7518444e57fa35b5642df4c7c5e322af4d2c8721172", size = 414555, upload-time = "2025-12-16T14:55:40.152Z" }, ] [[package]] name = "setuptools" version = "80.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958 } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486 }, + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "smmap" version = "5.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, ] [[package]] name = "soupsieve" version = "2.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/23/adf3796d740536d63a6fbda113d07e60c734b6ed5d3058d1e47fc0495e47/soupsieve-2.8.1.tar.gz", hash = "sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350", size = 117856 } +sdist = { url = "https://files.pythonhosted.org/packages/89/23/adf3796d740536d63a6fbda113d07e60c734b6ed5d3058d1e47fc0495e47/soupsieve-2.8.1.tar.gz", hash = "sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350", size = 117856, upload-time = "2025-12-18T13:50:34.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl", hash = "sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434", size = 36710 }, + { url = "https://files.pythonhosted.org/packages/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl", hash = "sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434", size = 36710, upload-time = "2025-12-18T13:50:33.267Z" }, ] [[package]] @@ -4120,23 +4086,23 @@ dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/f9/5e4491e5ccf42f5d9cfc663741d261b3e6e1683ae7812114e7636409fcc6/sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88", size = 9869912 } +sdist = { url = "https://files.pythonhosted.org/packages/be/f9/5e4491e5ccf42f5d9cfc663741d261b3e6e1683ae7812114e7636409fcc6/sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88", size = 9869912, upload-time = "2025-12-09T21:05:16.737Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/70/75b1387d72e2847220441166c5eb4e9846dd753895208c13e6d66523b2d9/sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85", size = 2154148 }, - { url = "https://files.pythonhosted.org/packages/d8/a4/7805e02323c49cb9d1ae5cd4913b28c97103079765f520043f914fca4cb3/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4", size = 3233051 }, - { url = "https://files.pythonhosted.org/packages/d7/ec/32ae09139f61bef3de3142e85c47abdee8db9a55af2bb438da54a4549263/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0", size = 3232781 }, - { url = "https://files.pythonhosted.org/packages/ad/bd/bf7b869b6f5585eac34222e1cf4405f4ba8c3b85dd6b1af5d4ce8bca695f/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0", size = 3182096 }, - { url = "https://files.pythonhosted.org/packages/21/6a/c219720a241bb8f35c88815ccc27761f5af7fdef04b987b0e8a2c1a6dcaa/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826", size = 3205109 }, - { url = "https://files.pythonhosted.org/packages/bd/c4/6ccf31b2bc925d5d95fab403ffd50d20d7c82b858cf1a4855664ca054dce/sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a", size = 2114240 }, - { url = "https://files.pythonhosted.org/packages/de/29/a27a31fca07316def418db6f7c70ab14010506616a2decef1906050a0587/sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7", size = 2137615 }, - { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672 }, + { url = "https://files.pythonhosted.org/packages/fe/70/75b1387d72e2847220441166c5eb4e9846dd753895208c13e6d66523b2d9/sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85", size = 2154148, upload-time = "2025-12-10T20:03:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a4/7805e02323c49cb9d1ae5cd4913b28c97103079765f520043f914fca4cb3/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4", size = 3233051, upload-time = "2025-12-09T22:06:04.768Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ec/32ae09139f61bef3de3142e85c47abdee8db9a55af2bb438da54a4549263/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0", size = 3232781, upload-time = "2025-12-09T22:09:54.435Z" }, + { url = "https://files.pythonhosted.org/packages/ad/bd/bf7b869b6f5585eac34222e1cf4405f4ba8c3b85dd6b1af5d4ce8bca695f/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0", size = 3182096, upload-time = "2025-12-09T22:06:06.169Z" }, + { url = "https://files.pythonhosted.org/packages/21/6a/c219720a241bb8f35c88815ccc27761f5af7fdef04b987b0e8a2c1a6dcaa/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826", size = 3205109, upload-time = "2025-12-09T22:09:55.969Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c4/6ccf31b2bc925d5d95fab403ffd50d20d7c82b858cf1a4855664ca054dce/sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a", size = 2114240, upload-time = "2025-12-09T21:29:54.007Z" }, + { url = "https://files.pythonhosted.org/packages/de/29/a27a31fca07316def418db6f7c70ab14010506616a2decef1906050a0587/sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7", size = 2137615, upload-time = "2025-12-09T21:29:55.85Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672, upload-time = "2025-12-09T21:54:52.608Z" }, ] [[package]] name = "sqlitedict" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846 } +sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846, upload-time = "2022-12-03T13:39:13.102Z" } [[package]] name = "sqlmodel" @@ -4146,18 +4112,18 @@ dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392, upload-time = "2024-08-31T09:43:24.088Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, + { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276, upload-time = "2024-08-31T09:43:22.358Z" }, ] [[package]] name = "sqlparse" version = "0.5.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815 } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138 }, + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, ] [[package]] @@ -4169,9 +4135,9 @@ dependencies = [ { name = "executing" }, { name = "pure-eval" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] [[package]] @@ -4182,9 +4148,9 @@ dependencies = [ { name = "anyio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033 }, + { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, ] [[package]] @@ -4209,11 +4175,11 @@ dependencies = [ { name = "toml" }, { name = "tornado" }, { name = "typing-extensions" }, - { name = "watchdog", marker = "platform_system != 'Darwin'" }, + { name = "watchdog", marker = "sys_platform != 'darwin'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/20/434aaceccc6e1912671d869926103051330437adba72d538d787a07727ef/streamlit-1.52.2.tar.gz", hash = "sha256:64a4dda8bc5cdd37bfd490e93bb53da35aaef946fcfc283a7980dacdf165108b", size = 8584178 } +sdist = { url = "https://files.pythonhosted.org/packages/43/20/434aaceccc6e1912671d869926103051330437adba72d538d787a07727ef/streamlit-1.52.2.tar.gz", hash = "sha256:64a4dda8bc5cdd37bfd490e93bb53da35aaef946fcfc283a7980dacdf165108b", size = 8584178, upload-time = "2025-12-17T17:07:59.642Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/95/6b7873f0267973ebd55ba9cd33a690b35a116f2779901ef6185a0e21864d/streamlit-1.52.2-py3-none-any.whl", hash = "sha256:a16bb4fbc9781e173ce9dfbd8ffb189c174f148f9ca4fb8fa56423e84e193fc8", size = 9025937 }, + { url = "https://files.pythonhosted.org/packages/c0/95/6b7873f0267973ebd55ba9cd33a690b35a116f2779901ef6185a0e21864d/streamlit-1.52.2-py3-none-any.whl", hash = "sha256:a16bb4fbc9781e173ce9dfbd8ffb189c174f148f9ca4fb8fa56423e84e193fc8", size = 9025937, upload-time = "2025-12-17T17:07:57.67Z" }, ] [[package]] @@ -4235,9 +4201,9 @@ dependencies = [ { name = "torch" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481, upload-time = "2025-09-23T21:41:37.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395 }, + { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395, upload-time = "2025-09-23T21:41:35.369Z" }, ] [[package]] @@ -4247,27 +4213,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] [[package]] name = "tabulate" version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, ] [[package]] name = "tenacity" version = "9.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248 }, + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, ] [[package]] @@ -4279,18 +4245,18 @@ dependencies = [ { name = "packaging" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2b/c5/d4cc6e293fb837aaf9f76dd7745476aeba8ef7ef5146c3b3f9ee375fe7a5/tensorboardx-2.6.4.tar.gz", hash = "sha256:b163ccb7798b31100b9f5fa4d6bc22dad362d7065c2f24b51e50731adde86828", size = 4769801 } +sdist = { url = "https://files.pythonhosted.org/packages/2b/c5/d4cc6e293fb837aaf9f76dd7745476aeba8ef7ef5146c3b3f9ee375fe7a5/tensorboardx-2.6.4.tar.gz", hash = "sha256:b163ccb7798b31100b9f5fa4d6bc22dad362d7065c2f24b51e50731adde86828", size = 4769801, upload-time = "2025-06-10T22:37:07.419Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/1d/b5d63f1a6b824282b57f7b581810d20b7a28ca951f2d5b59f1eb0782c12b/tensorboardx-2.6.4-py3-none-any.whl", hash = "sha256:5970cf3a1f0a6a6e8b180ccf46f3fe832b8a25a70b86e5a237048a7c0beb18e2", size = 87201 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/b5d63f1a6b824282b57f7b581810d20b7a28ca951f2d5b59f1eb0782c12b/tensorboardx-2.6.4-py3-none-any.whl", hash = "sha256:5970cf3a1f0a6a6e8b180ccf46f3fe832b8a25a70b86e5a237048a7c0beb18e2", size = 87201, upload-time = "2025-06-10T22:37:05.44Z" }, ] [[package]] name = "termcolor" version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434 } +sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734 }, + { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, ] [[package]] @@ -4299,21 +4265,21 @@ version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_system != 'Darwin' and sys_platform == 'linux') or (os_name == 'nt' and platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux') or (os_name == 'nt' and platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, ] [[package]] name = "threadpoolctl" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] [[package]] @@ -4324,15 +4290,15 @@ dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991 }, - { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798 }, - { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865 }, - { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856 }, - { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308 }, - { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697 }, - { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375 }, + { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991, upload-time = "2025-10-06T20:21:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798, upload-time = "2025-10-06T20:21:35.579Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865, upload-time = "2025-10-06T20:21:36.675Z" }, + { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856, upload-time = "2025-10-06T20:21:37.873Z" }, + { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308, upload-time = "2025-10-06T20:21:39.577Z" }, + { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697, upload-time = "2025-10-06T20:21:41.154Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375, upload-time = "2025-10-06T20:21:43.201Z" }, ] [[package]] @@ -4342,9 +4308,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, ] [[package]] @@ -4354,45 +4320,45 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, - { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301 }, - { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308 }, - { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964 }, - { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542 }, +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, ] [[package]] name = "toml" version = "0.10.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, ] [[package]] name = "tomli" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392 } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408 }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] [[package]] @@ -4404,30 +4370,30 @@ dependencies = [ { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux' and sys_platform != 'darwin'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681 }, - { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036 }, - { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861 }, - { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222 }, + { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681, upload-time = "2025-11-12T15:19:56.48Z" }, + { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036, upload-time = "2025-11-12T15:21:01.886Z" }, + { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861, upload-time = "2025-11-12T15:21:30.145Z" }, + { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222, upload-time = "2025-11-12T15:20:46.223Z" }, ] [[package]] @@ -4439,28 +4405,28 @@ dependencies = [ { name = "packaging" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144, upload-time = "2023-03-10T22:02:20.455Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162 }, + { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162, upload-time = "2023-03-10T22:02:18.818Z" }, ] [[package]] name = "tornado" version = "6.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632 } +sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632, upload-time = "2025-12-15T19:21:03.836Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909 }, - { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163 }, - { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746 }, - { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083 }, - { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315 }, - { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003 }, - { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412 }, - { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392 }, - { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481 }, - { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886 }, - { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910 }, + { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909, upload-time = "2025-12-15T19:20:48.382Z" }, + { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163, upload-time = "2025-12-15T19:20:49.791Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746, upload-time = "2025-12-15T19:20:51.491Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083, upload-time = "2025-12-15T19:20:52.778Z" }, + { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315, upload-time = "2025-12-15T19:20:53.996Z" }, + { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003, upload-time = "2025-12-15T19:20:56.101Z" }, + { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412, upload-time = "2025-12-15T19:20:57.398Z" }, + { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392, upload-time = "2025-12-15T19:20:58.692Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481, upload-time = "2025-12-15T19:21:00.008Z" }, + { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886, upload-time = "2025-12-15T19:21:01.287Z" }, + { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910, upload-time = "2025-12-15T19:21:02.571Z" }, ] [[package]] @@ -4468,20 +4434,20 @@ name = "tqdm" version = "4.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Windows' and sys_platform == 'linux') or (platform_system == 'Windows' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] [[package]] name = "traitlets" version = "5.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, ] [[package]] @@ -4492,9 +4458,9 @@ dependencies = [ { name = "torch" }, { name = "transformers", extra = ["sentencepiece", "torch"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754 } +sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754, upload-time = "2025-06-15T13:34:38.522Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073, upload-time = "2025-06-15T13:34:37.468Z" }, ] [[package]] @@ -4513,9 +4479,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680 } +sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680, upload-time = "2025-11-25T15:51:30.139Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463 }, + { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463, upload-time = "2025-11-25T15:51:26.493Z" }, ] [package.optional-dependencies] @@ -4533,8 +4499,7 @@ name = "triton" version = "3.5.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/2e/f95e673222afa2c7f0c687d8913e98fcf2589ef0b1405de76894e37fe18f/triton-3.5.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f63e34dcb32d7bd3a1d0195f60f30d2aee8b08a69a0424189b71017e23dfc3d2", size = 159821655 }, - { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692 }, + { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692, upload-time = "2025-11-11T17:40:46.074Z" }, ] [[package]] @@ -4547,18 +4512,18 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371 } +sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, ] [[package]] name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] @@ -4568,45 +4533,45 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] name = "tzdata" version = "2025.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521 }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] [[package]] name = "unidecode" version = "1.3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701 } +sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701, upload-time = "2024-01-11T11:58:35.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494 }, + { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494, upload-time = "2024-01-11T11:58:33.012Z" }, ] [[package]] name = "uri-template" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, ] [[package]] name = "urllib3" version = "2.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930 } +sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182 }, + { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, ] [[package]] @@ -4618,14 +4583,14 @@ dependencies = [ { name = "h11" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502 }, + { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, ] [package.optional-dependencies] standard = [ - { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "httptools" }, { name = "python-dotenv" }, { name = "pyyaml" }, @@ -4638,14 +4603,14 @@ standard = [ name = "uvloop" version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335 }, - { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903 }, - { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499 }, - { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133 }, - { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681 }, - { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261 }, + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" }, ] [[package]] @@ -4658,36 +4623,36 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799 } +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095 }, + { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, ] [[package]] name = "waitress" version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901 } +sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901, upload-time = "2024-11-16T20:02:35.195Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232 }, + { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232, upload-time = "2024-11-16T20:02:33.858Z" }, ] [[package]] name = "watchdog" version = "6.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] [[package]] @@ -4697,86 +4662,86 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318 }, - { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478 }, - { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894 }, - { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065 }, - { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377 }, - { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837 }, - { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456 }, - { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614 }, - { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690 }, - { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459 }, - { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663 }, - { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453 }, - { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611 }, - { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889 }, - { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616 }, - { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413 }, + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" }, ] [[package]] name = "wcwidth" version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293 } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286 }, + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, ] [[package]] name = "webcolors" version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, ] [[package]] name = "webencodings" version = "0.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, ] [[package]] name = "websocket-client" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, ] [[package]] name = "websockets" version = "15.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423 }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080 }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329 }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312 }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319 }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631 }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016 }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426 }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360 }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388 }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830 }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109 }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343 }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599 }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207 }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155 }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884 }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, + { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, + { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, + { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, + { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, + { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, + { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, + { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] [[package]] @@ -4786,18 +4751,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/ea/b0f8eeb287f8df9066e56e831c7824ac6bab645dd6c7a8f4b2d767944f9b/werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e", size = 864687 } +sdist = { url = "https://files.pythonhosted.org/packages/45/ea/b0f8eeb287f8df9066e56e831c7824ac6bab645dd6c7a8f4b2d767944f9b/werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e", size = 864687, upload-time = "2025-11-29T02:15:22.841Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/f9/9e082990c2585c744734f85bec79b5dae5df9c974ffee58fe421652c8e91/werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905", size = 224960 }, + { url = "https://files.pythonhosted.org/packages/2f/f9/9e082990c2585c744734f85bec79b5dae5df9c974ffee58fe421652c8e91/werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905", size = 224960, upload-time = "2025-11-29T02:15:21.13Z" }, ] [[package]] name = "widgetsnbextension" version = "4.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, ] [[package]] @@ -4807,59 +4772,59 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581480705f912040172f6570cc12e68a245ba9258c32ef/wikipedia_api-0.8.1.tar.gz", hash = "sha256:b31e93b3f5407c1a1ba413ed7326a05379a3c270df6cf6a211aca67a14c5658b", size = 19934 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581480705f912040172f6570cc12e68a245ba9258c32ef/wikipedia_api-0.8.1.tar.gz", hash = "sha256:b31e93b3f5407c1a1ba413ed7326a05379a3c270df6cf6a211aca67a14c5658b", size = 19934, upload-time = "2025-01-19T23:44:33.488Z" } [[package]] name = "wrapt" version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040 } +sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481 }, - { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692 }, - { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574 }, - { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688 }, - { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698 }, - { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096 }, - { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878 }, - { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298 }, - { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361 }, - { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035 }, - { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383 }, - { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894 }, - { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046 }, + { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481, upload-time = "2025-11-07T00:43:11.103Z" }, + { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692, upload-time = "2025-11-07T00:43:13.697Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574, upload-time = "2025-11-07T00:43:14.967Z" }, + { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688, upload-time = "2025-11-07T00:43:18.275Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698, upload-time = "2025-11-07T00:43:19.407Z" }, + { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096, upload-time = "2025-11-07T00:43:16.5Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878, upload-time = "2025-11-07T00:43:20.81Z" }, + { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298, upload-time = "2025-11-07T00:43:22.229Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361, upload-time = "2025-11-07T00:43:24.301Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035, upload-time = "2025-11-07T00:43:28.96Z" }, + { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383, upload-time = "2025-11-07T00:43:25.804Z" }, + { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894, upload-time = "2025-11-07T00:43:27.074Z" }, + { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, ] [[package]] name = "xmltodict" version = "0.14.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942 } +sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942, upload-time = "2024-10-16T06:10:29.683Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981 }, + { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981, upload-time = "2024-10-16T06:10:27.649Z" }, ] [[package]] name = "xxhash" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160 } +sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845 }, - { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807 }, - { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786 }, - { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830 }, - { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606 }, - { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872 }, - { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217 }, - { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139 }, - { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669 }, - { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018 }, - { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058 }, - { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628 }, - { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577 }, - { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487 }, - { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863 }, + { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" }, + { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" }, + { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" }, + { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" }, + { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" }, + { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" }, + { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" }, + { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" }, ] [[package]] @@ -4871,32 +4836,32 @@ dependencies = [ { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169 } +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517 }, - { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495 }, - { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400 }, - { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545 }, - { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598 }, - { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893 }, - { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240 }, - { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965 }, - { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026 }, - { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637 }, - { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082 }, - { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811 }, - { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223 }, - { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118 }, - { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852 }, - { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012 }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814 }, + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] [[package]] name = "zipp" version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ] From bdec740dc306f51fef7a639eefe8d7f1fc35cc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:03:31 -0300 Subject: [PATCH 034/101] Feature/anonymize document refactor (#73) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add render policy support and refactor anonymization logic for improved token rendering * 📝Update anonymization docs * ♻️ Refactor: modularize document anonymization * 📝 Rename notebook for document anonymization with render policy * FECHA disambiguation bug fixed, label and render policies changed and whole code reviewed for PR --- .../routers/anonymizer/anonymizer.py | 161 +++- aymurai/meta/api_interfaces.py | 11 +- aymurai/settings.py | 14 +- aymurai/text/anonymization.py | 766 ------------------ aymurai/text/anonymization/__init__.py | 7 + aymurai/text/anonymization/alignment.py | 396 +++++++++ aymurai/text/anonymization/doc_anonymizer.py | 88 ++ aymurai/text/anonymization/watermarks.py | 197 +++++ aymurai/text/anonymization/xml_docx.py | 172 ++++ aymurai/transforms/datetime_formatter/core.py | 2 +- docs/anonymization/README.md | 61 +- .../08-disambiguation-endpoint-smoke.ipynb | 2 - .../10-anonymize-document-render-policy.ipynb | 266 ++++++ 13 files changed, 1357 insertions(+), 786 deletions(-) delete mode 100644 aymurai/text/anonymization.py create mode 100644 aymurai/text/anonymization/__init__.py create mode 100644 aymurai/text/anonymization/alignment.py create mode 100644 aymurai/text/anonymization/doc_anonymizer.py create mode 100644 aymurai/text/anonymization/watermarks.py create mode 100644 aymurai/text/anonymization/xml_docx.py create mode 100644 notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index ec2b481e..9d963ab1 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -28,10 +28,11 @@ DocumentInformation, LabelPolicy, PromptLibrary, + RenderPolicy, TextRequest, ) from aymurai.settings import settings -from aymurai.text.anonymization import DocAnonymizer +from aymurai.text.anonymization import DocAnonymizer, replace_labels_in_text from aymurai.text.extraction import MIMETYPE_EXTENSION_MAPPER from aymurai.utils.entity_disambiguation import ( build_canonical_entities, @@ -105,6 +106,10 @@ def _merge_label_policies( current.disambiguation = incoming.disambiguation if incoming.anonymize is not None: current.anonymize = incoming.anonymize + if incoming.use_subclass_when_available is not None: + current.use_subclass_when_available = ( + incoming.use_subclass_when_available + ) policies[label] = current if request_policies: @@ -115,11 +120,131 @@ def _merge_label_policies( current.disambiguation = incoming.disambiguation if incoming.anonymize is not None: current.anonymize = incoming.anonymize + if incoming.use_subclass_when_available is not None: + current.use_subclass_when_available = ( + incoming.use_subclass_when_available + ) policies[label] = current return policies +def _merge_render_policy( + request_policy: RenderPolicy | None, +) -> RenderPolicy: + """ + Merges render policies from settings and request, with request policy taking precedence. + + Args: + request_policy (RenderPolicy | None): Render policy from request. + + Returns: + RenderPolicy: Effective render policy. + """ + policy = RenderPolicy( + suffix_mode="auto", + suffix_threshold=1, + ) + + def apply(incoming: RenderPolicy) -> None: + nonlocal policy + if incoming.suffix_mode is not None: + policy.suffix_mode = incoming.suffix_mode + if incoming.suffix_threshold is not None: + policy.suffix_threshold = incoming.suffix_threshold + + if settings.RENDER_POLICY: + apply(RenderPolicy.model_validate(settings.RENDER_POLICY)) + + if request_policy: + apply(RenderPolicy.model_validate(request_policy)) + + return policy + + +def _resolve_token_base( + label: DocLabel, + label_policy: LabelPolicy, +) -> str: + """ + Resolves the base token label for rendering. + + Args: + label (DocLabel): Label to render. + label_policy (LabelPolicy): Label policy rules. + + Returns: + str: Base token label (e.g., PER, DENUNCIANTE). + """ + attrs = label.attrs + if attrs is None: + return label.text + + subclass = None + if attrs.aymurai_label_subclass: + subclass = attrs.aymurai_label_subclass[0] + + if label_policy.use_subclass_when_available and subclass: + return subclass.upper() + + return attrs.aymurai_label + + +def _build_render_context( + annotations: list[DocumentInformation], + render_policy: RenderPolicy, + label_policies: dict[str, LabelPolicy], +) -> dict: + """ + Builds render context with per-entity indices and counts. + + Args: + annotations (list[DocumentInformation]): Document annotations. + render_policy (RenderPolicy): Render policy rules. + label_policies (dict[str, LabelPolicy]): Per-label policies. + + Returns: + dict: Render context with policy, indices, and counts. + """ + occurrences: list[tuple[int, int, str, str]] = [] + + for p_idx, paragraph in enumerate(annotations): + for label in paragraph.labels or []: + label_policy = label_policies.get( + label.attrs.aymurai_label + if label.attrs and label.attrs.aymurai_label + else None, + LabelPolicy(), + ) + base = _resolve_token_base(label, label_policy) + entity_id = ( + str(label.attrs.canonical_entity_id) + if label.attrs and label.attrs.canonical_entity_id + else label.text + ) + occurrences.append((p_idx, label.start_char, base, entity_id)) + + occurrences.sort(key=lambda item: (item[0], item[1])) + + index_by_entity: dict[tuple[str, str], int] = {} + next_index_by_base: dict[str, int] = {} + + for _, _, base, entity_id in occurrences: + key = (base, entity_id) + if key not in index_by_entity: + next_index_by_base[base] = next_index_by_base.get(base, 0) + 1 + index_by_entity[key] = next_index_by_base[base] + + count_by_base = {base: count for base, count in next_index_by_base.items()} + + return { + "render_policy": render_policy, + "label_policies": label_policies, + "index_by_entity": index_by_entity, + "count_by_base": count_by_base, + } + + def _should_anonymize_label( label: DocLabel, label_policies: dict[str, LabelPolicy], @@ -137,7 +262,11 @@ def _should_anonymize_label( if label.attrs and label.attrs.aymurai_anonymize is not None: return bool(label.attrs.aymurai_anonymize) - policy = label_policies.get(label.attrs.aymurai_label) if label.attrs else None + policy = ( + label_policies.get(str(label.attrs.aymurai_label).strip().upper()) + if label.attrs and label.attrs.aymurai_label + else None + ) if policy is None: return True @@ -265,7 +394,7 @@ async def anonymizer_disambiguate( ) labels = [label for paragraph in paragraphs for label in (paragraph.labels or [])] - effective_policies = _merge_label_policies(label_policies) + effective_label_policies = _merge_label_policies(label_policies) logger.info("disambiguation labels: %d", len(labels)) prompt_library = load_prompts_from_yaml() @@ -273,7 +402,10 @@ async def anonymizer_disambiguate( all_detected_labels = { label.attrs.aymurai_label for label in labels - if label.attrs and label.attrs.aymurai_label + if label.attrs + and label.attrs.aymurai_label + and effective_label_policies.get(label.attrs.aymurai_label) + and effective_label_policies.get(label.attrs.aymurai_label).anonymize } default_llm_labels = ( @@ -284,7 +416,7 @@ async def anonymizer_disambiguate( fuzzy_labels: set[str] = set() for label in all_detected_labels: - policy = effective_policies.get(label) + policy = effective_label_policies.get(label) if policy and policy.disambiguation == "llm": llm_labels.append(label) fuzzy_labels.add(label) @@ -407,7 +539,7 @@ async def anonymizer_disambiguate( label.attrs.aymurai_label, "fuzzy" ) - policy = effective_policies.get(label.attrs.aymurai_label) + policy = effective_label_policies.get(label.attrs.aymurai_label) label.attrs.aymurai_anonymize = ( policy.anonymize if policy and policy.anonymize is not None else True ) @@ -427,7 +559,7 @@ async def anonymizer_disambiguate( return DocumentAnnotations( data=predictions, - label_policies=effective_policies if effective_policies else None, + label_policies=effective_label_policies if effective_label_policies else None, ) @@ -503,7 +635,8 @@ async def anonymizer_compile_document( annots_json = json.loads(annotations) annots = DocumentAnnotations.model_validate(annots_json) logger.info(f"processing annotations => {annots}") - effective_policies = _merge_label_policies(annots.label_policies) + effective_label_policies = _merge_label_policies(annots.label_policies) + effective_render_policy = _merge_render_policy(annots.render_policy) # Add paragraphs to the database # validation MUST be at least an empty list, to remember user feedback @@ -534,7 +667,7 @@ async def anonymizer_compile_document( filtered_labels = [ label for label in (paragraph.labels or []) - if _should_anonymize_label(label, effective_policies) + if _should_anonymize_label(label, effective_label_policies) ] filtered_annotations.append( DocumentInformation( @@ -543,8 +676,13 @@ async def anonymizer_compile_document( ) ) + render_context = _build_render_context( + filtered_annotations, effective_render_policy, effective_label_policies + ) + if suffix == ".docx": item = {"path": tmp_filename} + doc_anonymizer.render_context = render_context doc_anonymizer( item, [ @@ -558,7 +696,10 @@ async def anonymizer_compile_document( else: # Export as raw document anonymized_doc = [ - doc_anonymizer.replace_labels_in_text(document_information.model_dump()) + replace_labels_in_text( + document_information.model_dump(), + render_context=render_context, + ) .replace("<", "<") .replace(">", ">") for document_information in filtered_annotations diff --git a/aymurai/meta/api_interfaces.py b/aymurai/meta/api_interfaces.py index 9fd601ce..1caa6509 100644 --- a/aymurai/meta/api_interfaces.py +++ b/aymurai/meta/api_interfaces.py @@ -48,8 +48,16 @@ class DocumentInformation(BaseModel): class LabelPolicy(BaseModel): """Per-label policy for disambiguation and anonymization.""" - disambiguation: Literal["none", "fuzzy", "llm"] | None = None anonymize: bool | None = None + disambiguation: Literal["none", "fuzzy", "llm"] | None = None + use_subclass_when_available: bool | None = None + + +class RenderPolicy(BaseModel): + """Render policy for anonymized tokens.""" + + suffix_mode: Literal["auto", "always", "never"] | None = None + suffix_threshold: int | None = None class DocumentAnnotations(BaseModel): @@ -57,6 +65,7 @@ class DocumentAnnotations(BaseModel): data: list[DocumentInformation] label_policies: dict[str, LabelPolicy] | None = None + render_policy: RenderPolicy | None = None class DataPublicDocumentAnnotations(RootModel): diff --git a/aymurai/settings.py b/aymurai/settings.py index cfaee728..85fe08e4 100644 --- a/aymurai/settings.py +++ b/aymurai/settings.py @@ -3,8 +3,8 @@ from pathlib import Path from dotenv import load_dotenv +from pydantic import ConfigDict, FilePath, field_validator from pydantic_settings import BaseSettings -from pydantic import FilePath, ConfigDict, field_validator import aymurai @@ -88,6 +88,18 @@ def parse_label_policies(cls, v): return json.loads(v) return v + # Render policy (JSON dict) + RENDER_POLICY: dict | None = None + + @field_validator("RENDER_POLICY", mode="before") + @classmethod + def parse_render_policy(cls, v): + if v is None or v == "": + return None + if isinstance(v, str): + return json.loads(v) + return v + load_env() settings = Settings() diff --git a/aymurai/text/anonymization.py b/aymurai/text/anonymization.py deleted file mode 100644 index fe6fb14f..00000000 --- a/aymurai/text/anonymization.py +++ /dev/null @@ -1,766 +0,0 @@ -import os -import re -import tempfile -import xml.sax.saxutils -import zipfile -from copy import deepcopy -from glob import glob -from unicodedata import normalize - -import numpy as np -import pandas as pd -from docx import Document -from docx.enum.text import WD_ALIGN_PARAGRAPH -from docx.opc.constants import RELATIONSHIP_TYPE -from docx.oxml.shared import OxmlElement, qn -from docx.shared import Inches, Pt, RGBColor -from jiwer import cer -from joblib import hash -from lxml import etree -from more_itertools import flatten - -from aymurai.logger import get_logger -from aymurai.meta.pipeline_interfaces import Transform -from aymurai.models.flair.utils import FlairTextNormalize -from aymurai.utils.alignment.core import align_text, tokenize -from aymurai.utils.cache import cache_load, cache_save, get_cache_key - -logger = get_logger(__file__) - - -REGEX_PARAGRAPH = r"((?.*?)(\/w:p\b)" -REGEX_FRAGMENT = r"(?(?P.*?)(<.*?\/w:t)" - - -class DocAnonymizer(Transform): - """ - Anonymize document by replacing sensitive data with label tokens - """ - - def __init__(self, use_cache: bool = False, **kwargs): - self.use_cache = use_cache - self.kwargs = kwargs - - def unzip_document(self, doc_path: str, output_dir: str) -> None: - """ - Unzips the document file to the specified output directory. - - Args: - doc_path (str): The path to the document file. - output_dir (str): The directory where the contents of the document - file will be extracted. - """ - # Ensure the output directory exists - os.makedirs(output_dir, exist_ok=True) - - # Open the doc file as a zip file - with zipfile.ZipFile(doc_path, "r") as doc_zip: - # Extract all the contents to the output directory - doc_zip.extractall(output_dir) - logger.info(f"unzipped {doc_path} to {output_dir}") - - def index_paragraphs(self, file: str) -> list[dict]: - """ - Indexes the paragraphs of an XML file. - - Args: - file (str): The path to the XML file to be indexed. - - Returns: - list[dict]: A list of dictionaries representing the indexed paragraphs. - """ - # Read the XML file - with open(file) as f: - xml = f.read() - - paragraphs = [] - paragraph_index = 0 - - # Find all paragraphs in the XML file - for match in re.finditer(REGEX_PARAGRAPH, xml): - paragraph = match.group("paragraph") - paragraph_start = match.start("paragraph") - paragraph_end = match.end("paragraph") - fragments = [] - fragment_index = 0 - - # Find all text fragments in the paragraph - for fragment in re.finditer(REGEX_FRAGMENT, paragraph): - text = fragment.group("text") - start = fragment.start("text") - end = fragment.end("text") - - fragment_dict = { - "text": text, - "normalized_text": FlairTextNormalize.normalize_text(text), - "start": start, - "end": end, - "fragment_index": fragment_index, - "paragraph_index": paragraph_index, - } - fragments.append(fragment_dict) - fragment_index += 1 - - # Join all fragments as plain text - plain_text = "".join( - [fragment["normalized_text"] for fragment in fragments] - ) - - paragraphs.append( - { - "plain_text": plain_text, - "metadata": { - "start": paragraph_start, - "end": paragraph_end, - "fragments": fragments, - "xml_file": os.path.basename(file), - }, - } - ) - paragraph_index += 1 - - return paragraphs - - def match_paragraphs_with_predictions( - self, - paragraphs: list[dict], - predictions: list[dict], - ) -> list[dict]: - """ - Matches each paragraph with its corresponding predictions. - - Args: - paragraphs (list[dict]): A list of dictionaries representing the paragraphs. - predictions (list[dict]): A list of dictionaries representing - the predictions. - - Returns: - list[dict]: A list of dictionaries representing - the matched paragraphs with predictions. - """ - - paragraphs = deepcopy(paragraphs) - - # Hash prediction documents - pred_hashes = [hash(prediction["document"]) for prediction in predictions] - idx2hash = {i: _hash for i, _hash in enumerate(pred_hashes)} - - # Hash paragraphs - paragraphs = [ - paragraph - | {"hash": hash(normalize("NFKC", paragraph["plain_text"].strip()))} - for paragraph in paragraphs - ] - - # Assign prediction indices to each paragraph by hash - hash2idx = { - paragraph["hash"]: np.where(np.array(pred_hashes) == paragraph["hash"])[ - 0 - ].tolist() - for paragraph in paragraphs - } - - paragraphs = [ - paragraph | {"pred_indices": hash2idx[paragraph["hash"]]} - for paragraph in paragraphs - ] - - # Identify missing indices - missing_indices = list( - set(idx2hash.keys()) - set(flatten(list(hash2idx.values()))) - ) - - if missing_indices: - # Assign prediction indices to each paragraph by lowest CER - target_texts = np.array( - [prediction["document"] for prediction in predictions] - )[missing_indices] - - missing_paragraphs = [ - paragraph for paragraph in paragraphs if not paragraph["pred_indices"] - ] - - for missing_paragraph in missing_paragraphs: - source_text = missing_paragraph["plain_text"] - min_cer_idx = np.argmin( - [cer(source_text, target_text) for target_text in target_texts] - ) - missing_paragraph["pred_indices"] = [missing_indices[min_cer_idx]] - - # Assign document text and labels - paragraphs = [ - paragraph - | { - "document": predictions[paragraph["pred_indices"][0]]["document"], - "labels": predictions[paragraph["pred_indices"][0]]["labels"], - } - for paragraph in paragraphs - ] - - return paragraphs - - def unify_consecutive_labels( - self, sample: dict, text_key: str = "document" - ) -> list[dict]: - """ - Unifies consecutive labels in a sample. - - Args: - sample (dict): A dictionary representing the sample. - text_key (str, optional): The key for the text in the sample dictionary. - Defaults to "document". - - Returns: - list[dict]: A list of dictionaries representing the unified labels. - """ - sample = deepcopy(sample) - - # Extract labels and document text - labels = sample["labels"] - document = sample[text_key] - - # Reorder labels based on start indices - labels = sorted(labels, key=lambda x: x["start_char"]) - - unified_labels = [] - current_group = None - - # Iterate over labels - for label in labels: - # Get attributes - text = label["attrs"]["aymurai_alt_text"] or label["text"] - start_char = label["attrs"]["aymurai_alt_start_char"] or label["start_char"] - end_char = label["attrs"]["aymurai_alt_end_char"] or label["end_char"] - aymurai_label = label["attrs"]["aymurai_label"] - - if current_group is None: - # Start a new group with the current label - current_group = { - "text": text, - "start_char": start_char, - "end_char": end_char, - "aymurai_label": aymurai_label, - } - elif ( - current_group["aymurai_label"] == aymurai_label - and (start_char - current_group["end_char"]) <= 1 - ): - # Extend the current group with the current label - current_group["end_char"] = end_char - else: - # Finish the current group and start a new one - current_group["text"] = document[ - current_group["start_char"] : current_group["end_char"] + 1 - ] - unified_labels.append(current_group) - current_group = { - "text": text, - "start_char": start_char, - "end_char": end_char, - "aymurai_label": aymurai_label, - } - - # Finish the last group - if current_group is not None: - current_group["text"] = document[ - current_group["start_char"] : current_group["end_char"] + 1 - ] - unified_labels.append(current_group) - - return unified_labels - - def replace_labels_in_text(self, pred: dict, text_key: str = "document") -> str: - """ - Replaces labels in the text with anonymized tokens. - - Args: - pred (dict): A dictionary representing the prediction. - text_key (str, optional): The key for the text in the prediction dictionary. - Defaults to "document". - - Returns: - str: The text with replaced labels. - """ - pred = deepcopy(pred) - doc = pred[text_key] - - # Unify consecutive labels - unified_labels = self.unify_consecutive_labels(pred, text_key) - - # Initialize the offset - offset = 0 - - # Replace labels in the text - for unified_label in unified_labels: - # Adjust start and end character indices of the label - start_char = unified_label["start_char"] + offset - end_char = unified_label["end_char"] + offset - len_text_to_replace = end_char - start_char - - # Replace the text with the anonymized token - aymurai_label = f" <{unified_label['aymurai_label']}>" - len_aymurai_label = len(aymurai_label) - - doc = doc[:start_char] + aymurai_label + doc[end_char:] - - # Update the offset - offset += len_aymurai_label - len_text_to_replace - - return re.sub(r" +", " ", doc).strip() - - def erase_duplicates_justseen(self, series: pd.Series) -> pd.Series: - """ - Erases duplicates that were just seen between two consecutive rows. - - Args: - series (pd.Series): The pandas Series to be processed. - - Returns: - pd.Series: The processed pandas Series. - """ - return pd.Series( - [ - ( - "" - if (i > 0 and series.iloc[i] == series.iloc[i - 1]) - else series.iloc[i] - ) - for i in range(len(series)) - ] - ) - - def parse_token_indices(self, sample: dict) -> pd.DataFrame: - """ - Parses the token indices from a sample. - - Args: - sample (dict): A dictionary representing the sample. - - Returns: - pd.DataFrame: A pandas DataFrame representing the parsed token indices. - """ - original_text = " ".join( - [fragment["text"] for fragment in sample["metadata"]["fragments"]] - ) - anonymized_text = self.replace_labels_in_text(sample) - - aligned = align_text( - " " + original_text + " ", - " " + anonymized_text + " ", - ) - aligned["target"] = self.erase_duplicates_justseen(aligned["target"]) - - xml_file = sample["metadata"]["xml_file"] - - tokens = [] - for i, fragment in enumerate(sample["metadata"]["fragments"]): - text = fragment["text"] - tokenized_text = tokenize(text) - paragraph_index = fragment["paragraph_index"] - - # Use re.finditer to locate each instance of tokens in the text - token_matches = list( - re.finditer(r"\S+", text) - ) # \S+ matches any non-whitespace sequence - - # Loop over tokenized text and token_matches in parallel - for j, (token, match) in enumerate(zip(tokenized_text, token_matches)): - start = sample["metadata"]["start"] + fragment["start"] + match.start() - end = start + len(token) - - tokens.append((xml_file, paragraph_index, i, j, token, start, end)) - - tokens = pd.DataFrame( - tokens, - columns=[ - "xml_file", - "paragraph_index", - "fragment_index", - "token_index", - "token", - "start_char", - "end_char", - ], - ) - - tokens = pd.concat( - [tokens, aligned["target"].iloc[1:-1].reset_index(drop=True)], axis=1 - ) - - tokens["target"] = tokens["target"].fillna("") - - return tokens - - def normalize_document(self, xml_content: str) -> str: - """ - Normalizes the XML document by removing extra spaces, preserving line breaks, - and removing hyperlinks while preserving text content. - - Args: - xml_content (str): The XML content to be normalized. - - Returns: - str: The normalized XML content. - """ - # Parse the XML content - parser = etree.XMLParser(ns_clean=True) - root = etree.fromstring(xml_content.encode("utf-8"), parser) - - # Extract namespaces - namespaces = {k: v for k, v in root.nsmap.items() if k} - - # Remove hyperlinks but preserve the text content - for hyperlink in root.xpath("//w:hyperlink", namespaces=namespaces): - parent = hyperlink.getparent() - index = list(parent).index(hyperlink) - - # Move all text-containing children (e.g., w:r elements) outside the hyperlink tag - for child in hyperlink: - parent.insert(index, child) - index += 1 - parent.remove(hyperlink) # Remove the element itself - - # Process each paragraph - for wp in root.xpath("//w:p", namespaces=namespaces): - first = True - - # Find all w:r elements containing w:t elements - for wr in wp.xpath(".//w:r", namespaces=namespaces): - wt = wr.find(".//w:t", namespaces) - if wt is not None and wt.text: - # Normalize spaces within the text, preserving new line breaks - wt.text = re.sub(r"[^\S\r\n]+", " ", wt.text) - - # Add a leading space if not the first fragment in the paragraph - if not first: - wt.text = " " + wt.text.lstrip() - else: - wt.text = wt.text.lstrip() - first = False - - # Remove trailing spaces from all fragments - wt.text = wt.text.rstrip() - - # Set the xml:space attribute to preserve - wt.set( - "{http://www.w3.org/XML/1998/namespace}space", - "preserve", - ) - - # Check if the text is empty after normalization - if not wt.text or wt.text.strip() == "": - # Remove the w:r element from its parent - wr.getparent().remove(wr) - - # Write back the XML content to a string - xml_str = etree.tostring(root, encoding="unicode", pretty_print=True) - - return xml_str - - def replace_text_in_xml(self, paragraphs: list[dict], base_dir: str) -> None: - """ - Replaces text in XML files based on the provided paragraphs - and saves the modified files. - - Args: - paragraphs (list[dict]): A list of dictionaries representing - the paragraphs to be replaced. - base_dir (str): The base directory where the XML files are located. - """ - tokens = pd.concat( - [self.parse_token_indices(sample) for sample in paragraphs], - ignore_index=True, - ) - - fragments = ( - tokens.groupby(["xml_file", "paragraph_index", "fragment_index"]) - .agg({"target": " ".join, "start_char": "min", "end_char": "max"}) - .reset_index() - ) - - for xml_file, group in fragments.groupby("xml_file"): - group = group.sort_values("end_char", ascending=False) - - with open(f"{base_dir}/word/{xml_file}", "r+") as file: - content = file.read() - - for _, r in group.iterrows(): - start_char = r["start_char"] - end_char = r["end_char"] - - target = r["target"] - target = re.sub(r"[^\S\r\n]+", " ", target) - - # Escape XML special characters - target = xml.sax.saxutils.escape(target) - - content = content[:start_char] + target + content[end_char:] - - # MUST be at the end to dont screw up the indexes - content = self.normalize_document(content) - - file.seek(0) - file.write(content) - file.truncate() - - def add_files_to_zip(self, zip_file: zipfile.ZipFile, directory: str) -> None: - """ - Adds all files in the specified directory to a zip file. - - Args: - zip_file (zipfile.ZipFile): The zip file to add the files to. - directory (str): The directory containing the files to be added. - """ - for root, _, files in os.walk(directory): - for file in files: - file_path = os.path.join(root, file) - zip_file.write(file_path, os.path.relpath(file_path, directory)) - - def add_hyperlink( - self, - paragraph, - text, - url, - font_name="Archivo", - size=10, - color=RGBColor(115, 190, 250), - italic=False, - bold=True, - underline=True, - ) -> None: - """ - Adds a formatted hyperlink to a given paragraph in a Word document. - - Notes: - - This method directly manipulates the underlying XML of the paragraph to insert a hyperlink, - as python-docx does not natively support hyperlinks. - - The hyperlink will be appended to the end of the given paragraph. - - Formatting options (font, size, color, italic, bold, underline) are applied to the hyperlink text. - - Args: - paragraph: The python-docx paragraph object to which the hyperlink will be added. - text (str): The display text for the hyperlink. - url (str): The URL that the hyperlink points to. - font_name (str, optional): The font name to use for the hyperlink text. Defaults to "Archivo". - size (int, optional): The font size (in points) for the hyperlink text. Defaults to 10. - color (RGBColor, optional): The font color as an RGBColor tuple. Defaults to RGBColor(115, 190, 250). - italic (bool, optional): Whether the hyperlink text should be italicized. Defaults to False. - bold (bool, optional): Whether the hyperlink text should be bold. Defaults to True. - underline (bool, optional): Whether the hyperlink text should be underlined. Defaults to True. - - Raises: - ValueError: If the paragraph is not a valid python-docx paragraph object. - """ # noqa: E501 - # Create the hyperlink relationship - part = paragraph.part - r_id = part.relate_to(url, RELATIONSHIP_TYPE.HYPERLINK, is_external=True) - - # Create the hyperlink element - hyperlink = OxmlElement("w:hyperlink") - hyperlink.set(qn("r:id"), r_id) - - # Create a new run - new_run = OxmlElement("w:r") - - # Set run properties (formatting) - rPr = OxmlElement("w:rPr") - - # Set font - if font_name: - font = OxmlElement("w:rFonts") - font.set(qn("w:ascii"), font_name) - font.set(qn("w:hAnsi"), font_name) - rPr.append(font) - - # Set color - if color: - c = OxmlElement("w:color") - c.set(qn("w:val"), f"{color[0]:02x}{color[1]:02x}{color[2]:02x}") - rPr.append(c) - - # Set italic - if italic: - i = OxmlElement("w:i") - rPr.append(i) - - # Set bold - if bold: - b = OxmlElement("w:b") - rPr.append(b) - - # Set underline - added for links - if underline: - u = OxmlElement("w:u") - u.set(qn("w:val"), "single") # single underline - rPr.append(u) - - # Set size - sz = OxmlElement("w:sz") - sz.set(qn("w:val"), str(size * 2)) # Word uses half-points - rPr.append(sz) - - # Add properties to run - new_run.append(rPr) - - # Set text - t = OxmlElement("w:t") - t.text = text - new_run.append(t) - - # Add run to hyperlink - hyperlink.append(new_run) - - # Add hyperlink to paragraph - paragraph._p.append(hyperlink) - - def _add_watermark_to_footer( - self, - footer, - alignment, - font_name="Archivo", - hyperlink_text="AymurAI", - hyperlink_url="https://www.aymurai.info/", - watermark_text="Documento anonimizado por AymurAI", - ) -> None: - """ - Adds a watermark text to the footer of a document. - - Args: - footer: The footer object to which the watermark will be added. - alignment: The alignment setting for the paragraph (e.g., left, center, right). - font_name (str, optional): The font name to use for the watermark text. Defaults to "Archivo". - hyperlink_text (str, optional): The text to be hyperlinked. Defaults to "AymurAI". - hyperlink_url (str, optional): The URL to link "AymurAI" to. Defaults to "https://www.aymurai.info/". - watermark_text (str): The text to be used as the watermark. Defaults to "Documento anonimizado por AymurAI". - """ # noqa: E501 - paragraph = footer.add_paragraph() - paragraph.alignment = alignment - - if hyperlink_url and hyperlink_text in watermark_text: - parts = watermark_text.split(hyperlink_text, 1) - before_text = parts[0] - after_text = parts[1] if len(parts) > 1 else "" - - # Add text before the hyperlink - if before_text: - run = paragraph.add_run(before_text) - run.font.name = "Archivo" - run.font.color.rgb = RGBColor(192, 192, 192) - run.font.size = Pt(10) - - # Add hyperlink - self.add_hyperlink(paragraph, hyperlink_text, hyperlink_url) - - # Add text after the hyperlink - if after_text: - run = paragraph.add_run(after_text) - run.font.name = font_name - run.font.color.rgb = RGBColor(192, 192, 192) - run.font.size = Pt(10) - - else: - # Just add the full text without a hyperlink - run = paragraph.add_run(watermark_text) - run.font.name = font_name - run.font.color.rgb = RGBColor(192, 192, 192) - run.font.size = Pt(10) - - def add_footer_watermark(self, doc_path) -> None: - """ - Adds a watermark to the footer of each section in a Word document. - - Args: - doc_path: Path to the document - """ - document = Document(doc_path) - processed_footers = set() - - for section in document.sections: - section.footer_distance = Inches(0.1) - - # List of (footer_obj, alignment) tuples - footers = [(section.footer, WD_ALIGN_PARAGRAPH.RIGHT)] # Odd/default - if section.even_page_footer is not None: - footers.append((section.even_page_footer, WD_ALIGN_PARAGRAPH.LEFT)) - if section.different_first_page_header_footer: - footers.append((section.first_page_footer, WD_ALIGN_PARAGRAPH.RIGHT)) - - for footer, alignment in footers: - if id(footer) not in processed_footers: - self._add_watermark_to_footer(footer, alignment) - processed_footers.add(id(footer)) - - document.save(doc_path) - - def create_docx(self, xml_directory, output_file) -> None: - """ - Creates a new DOCX file by adding XML components from the specified directory. - - Args: - xml_directory (str): The directory containing the XML components. - output_file (str): The path to the output DOCX file. - """ - # Create a new zip file - with zipfile.ZipFile(output_file, "w") as docx: - # Add XML components - self.add_files_to_zip(docx, xml_directory) - - def __call__(self, item: dict, preds: list[dict], output_dir: str = ".") -> None: - """ - Performs the anonymization process on a document. - - Args: - item (dict): The document item to be anonymized. - preds (list[dict]): The list of predictions for the document. - output_dir (str, optional): The directory to save the anonymized document. - Defaults to ".". - - Raises: - ValueError: If the document has an extension other than `.docx`. - """ - item_path = item["path"] - - if not os.path.splitext(item_path)[-1] == ".docx": - raise ValueError("Only `.docx` extension is allowed.") - - if not item.get("data"): - item["data"] = {} - - cache_key = get_cache_key(item_path, self.__name__) - if self.use_cache and (cache_data := cache_load(key=cache_key)): - paragraphs = cache_data - else: - # Unzip document into a temporary directory - with tempfile.TemporaryDirectory() as tempdir: - self.unzip_document(item_path, tempdir) - - # Parse XML files - xml_files = glob(f"{tempdir}/**/*.xml", recursive=True) - paragraphs = (self.index_paragraphs(file) for file in xml_files) - paragraphs = list(flatten(paragraphs)) - - # Filter out empty paragraphs - paragraphs = [ - paragraph - for paragraph in paragraphs - if paragraph["plain_text"].strip() - ] - - # Matching - paragraphs = self.match_paragraphs_with_predictions(paragraphs, preds) - - # Edit XML filess - self.replace_text_in_xml(paragraphs, tempdir) - - # Recreate anonymized document - os.makedirs(output_dir, exist_ok=True) - self.create_docx( - tempdir, - f"{output_dir}/{os.path.basename(item_path)}", - ) - - # Add watermark to the footer - self.add_footer_watermark(f"{output_dir}/{os.path.basename(item_path)}") - - if self.use_cache: - cache_save(paragraphs, key=cache_key) diff --git a/aymurai/text/anonymization/__init__.py b/aymurai/text/anonymization/__init__.py new file mode 100644 index 00000000..7f839a95 --- /dev/null +++ b/aymurai/text/anonymization/__init__.py @@ -0,0 +1,7 @@ +from aymurai.text.anonymization.alignment import replace_labels_in_text +from aymurai.text.anonymization.doc_anonymizer import DocAnonymizer + +__all__ = [ + "DocAnonymizer", + "replace_labels_in_text", +] diff --git a/aymurai/text/anonymization/alignment.py b/aymurai/text/anonymization/alignment.py new file mode 100644 index 00000000..3a6386b3 --- /dev/null +++ b/aymurai/text/anonymization/alignment.py @@ -0,0 +1,396 @@ +import os +import re +from copy import deepcopy +from unicodedata import normalize + +import numpy as np +import pandas as pd +from jiwer import cer +from joblib import hash +from more_itertools import flatten + +from aymurai.models.flair.utils import FlairTextNormalize +from aymurai.utils.alignment.core import align_text, tokenize +from aymurai.meta.api_interfaces import LabelPolicy + +REGEX_PARAGRAPH = r"((?.*?)(\/w:p\b)" +REGEX_FRAGMENT = r"(?(?P.*?)(<.*?\/w:t)" + + +def resolve_render_token(label: dict, render_context: dict | None = None) -> str: + """ + Resolves the render token for a label using the current render context. + + Args: + label (dict): Label dictionary with attrs. + + Returns: + str: Render token to insert in the document. + """ + if not render_context: + return label["attrs"]["aymurai_label"] + + render_policy = render_context["render_policy"] + label_policies = render_context["label_policies"] + count_by_base = render_context["count_by_base"] + index_by_entity = render_context["index_by_entity"] + + attrs = label.get("attrs") or {} + base = attrs.get("aymurai_label") + + label_policy = label_policies.get(base.upper(), LabelPolicy()) + + subclasses = attrs.get("aymurai_label_subclass") or [] + if label_policy.use_subclass_when_available and subclasses: + base = subclasses[0].upper() + + if not base: + base = label.get("label") or label.get("text") or "ENT" + + entity_id = attrs.get("canonical_entity_id") or label.get("text") + key = (base, str(entity_id)) + index = index_by_entity.get(key) + + if render_policy.suffix_mode == "never" or index is None: + return base + + if render_policy.suffix_mode == "auto": + if count_by_base.get(base, 0) <= render_policy.suffix_threshold: + return base + + return f"{base}_{index}" + + +def unify_consecutive_labels( + sample: dict, + text_key: str = "document", + render_context: dict | None = None, +) -> list[dict]: + """ + Unifies consecutive labels in a sample. + + Args: + sample (dict): A dictionary representing the sample. + text_key (str, optional): The key for the text in the sample dictionary. + Defaults to "document". + render_context (dict | None, optional): The context for rendering labels. Defaults to None. + + Returns: + list[dict]: A list of dictionaries representing the unified labels. + """ + sample = deepcopy(sample) + + # Extract labels and document text + labels = sample["labels"] + document = sample[text_key] + + # Reorder labels based on start indices + labels = sorted(labels, key=lambda x: x["start_char"]) + + unified_labels = [] + current_group = None + + # Iterate over labels + for label in labels: + # Get attributes + text = label["attrs"]["aymurai_alt_text"] or label["text"] + start_char = label["attrs"]["aymurai_alt_start_char"] or label["start_char"] + end_char = label["attrs"]["aymurai_alt_end_char"] or label["end_char"] + aymurai_label = resolve_render_token(label, render_context) + + if current_group is None: + # Start a new group with the current label + current_group = { + "text": text, + "start_char": start_char, + "end_char": end_char, + "aymurai_label": aymurai_label, + } + elif ( + current_group["aymurai_label"] == aymurai_label + and (start_char - current_group["end_char"]) <= 1 + ): + # Extend the current group with the current label + current_group["end_char"] = end_char + else: + # Finish the current group and start a new one + current_group["text"] = document[ + current_group["start_char"] : current_group["end_char"] + 1 + ] + unified_labels.append(current_group) + current_group = { + "text": text, + "start_char": start_char, + "end_char": end_char, + "aymurai_label": aymurai_label, + } + + # Finish the last group + if current_group is not None: + current_group["text"] = document[ + current_group["start_char"] : current_group["end_char"] + 1 + ] + unified_labels.append(current_group) + + return unified_labels + + +def replace_labels_in_text( + pred: dict, + text_key: str = "document", + render_context: dict | None = None, +) -> str: + """ + Replaces labels in the text with anonymized tokens. + + Args: + pred (dict): A dictionary representing the prediction. + text_key (str, optional): The key for the text in the prediction dictionary. + Defaults to "document". + render_context (dict | None, optional): The context for rendering labels. Defaults to None. + Returns: + str: The text with replaced labels. + """ + pred = deepcopy(pred) + doc = pred[text_key] + + # Unify consecutive labels + unified_labels = unify_consecutive_labels( + pred, render_context=render_context, text_key=text_key + ) + + # Initialize the offset + offset = 0 + + # Replace labels in the text + for unified_label in unified_labels: + # Adjust start and end character indices of the label + start_char = unified_label["start_char"] + offset + end_char = unified_label["end_char"] + offset + len_text_to_replace = end_char - start_char + + # Replace the text with the anonymized token + aymurai_label = f" <{unified_label['aymurai_label']}>" + len_aymurai_label = len(aymurai_label) + + doc = doc[:start_char] + aymurai_label + doc[end_char:] + + # Update the offset + offset += len_aymurai_label - len_text_to_replace + + return re.sub(r" +", " ", doc).strip() + + +def erase_duplicates_justseen(series: pd.Series) -> pd.Series: + """ + Replaces consecutive duplicate values in a pandas Series with an empty string, keeping only the first occurrence. + + Args: + series (pd.Series): The input pandas Series. + + Returns: + pd.Series: A pandas Series with consecutive duplicates replaced by an empty string. + """ + return series.where(series.ne(series.shift(), fill_value=None), "") + + +def parse_token_indices( + sample: dict, render_context: dict | None = None +) -> pd.DataFrame: + """ + Parses the token indices from a sample. + + Args: + sample (dict): A dictionary representing the sample. + render_context (dict | None, optional): The context for rendering labels. Defaults to None. + + Returns: + pd.DataFrame: A pandas DataFrame representing the parsed token indices. + """ + original_text = " ".join( + [fragment["text"] for fragment in sample["metadata"]["fragments"]] + ) + anonymized_text = replace_labels_in_text(sample, render_context=render_context) + + aligned = align_text( + " " + original_text + " ", + " " + anonymized_text + " ", + ) + aligned["target"] = erase_duplicates_justseen(aligned["target"]) + + xml_file = sample["metadata"]["xml_file"] + + tokens = [] + for i, fragment in enumerate(sample["metadata"]["fragments"]): + text = fragment["text"] + tokenized_text = tokenize(text) + paragraph_index = fragment["paragraph_index"] + + # Use re.finditer to locate each instance of tokens in the text + token_matches = list( + re.finditer(r"\S+", text) + ) # \S+ matches any non-whitespace sequence + + # Loop over tokenized text and token_matches in parallel + for j, (token, match) in enumerate(zip(tokenized_text, token_matches)): + start = sample["metadata"]["start"] + fragment["start"] + match.start() + end = start + len(token) + + tokens.append((xml_file, paragraph_index, i, j, token, start, end)) + + tokens = pd.DataFrame( + tokens, + columns=[ + "xml_file", + "paragraph_index", + "fragment_index", + "token_index", + "token", + "start_char", + "end_char", + ], + ) + + tokens = pd.concat( + [tokens, aligned["target"].iloc[1:-1].reset_index(drop=True)], axis=1 + ) + + tokens["target"] = tokens["target"].fillna("") + + return tokens + + +def index_paragraphs(file: str) -> list[dict]: + """ + Indexes the paragraphs of an XML file. + + Args: + file (str): The path to the XML file to be indexed. + + Returns: + list[dict]: A list of dictionaries representing the indexed paragraphs. + """ + # Read the XML file + with open(file) as f: + xml = f.read() + + paragraphs = [] + paragraph_index = 0 + + # Find all paragraphs in the XML file + for match in re.finditer(REGEX_PARAGRAPH, xml): + paragraph = match.group("paragraph") + paragraph_start = match.start("paragraph") + paragraph_end = match.end("paragraph") + fragments = [] + fragment_index = 0 + + # Find all text fragments in the paragraph + for fragment in re.finditer(REGEX_FRAGMENT, paragraph): + text = fragment.group("text") + start = fragment.start("text") + end = fragment.end("text") + + fragment_dict = { + "text": text, + "normalized_text": FlairTextNormalize.normalize_text(text), + "start": start, + "end": end, + "fragment_index": fragment_index, + "paragraph_index": paragraph_index, + } + fragments.append(fragment_dict) + fragment_index += 1 + + # Join all fragments as plain text + plain_text = "".join([fragment["normalized_text"] for fragment in fragments]) + + paragraphs.append( + { + "plain_text": plain_text, + "metadata": { + "start": paragraph_start, + "end": paragraph_end, + "fragments": fragments, + "xml_file": os.path.basename(file), + }, + } + ) + paragraph_index += 1 + + return paragraphs + + +def match_paragraphs_with_predictions( + paragraphs: list[dict], + predictions: list[dict], +) -> list[dict]: + """ + Matches each paragraph with its corresponding predictions. + + Args: + paragraphs (list[dict]): A list of dictionaries representing the paragraphs. + predictions (list[dict]): A list of dictionaries representing + the predictions. + + Returns: + list[dict]: A list of dictionaries representing + the matched paragraphs with predictions. + """ + + paragraphs = deepcopy(paragraphs) + + # Hash prediction documents + pred_hashes = [hash(prediction["document"]) for prediction in predictions] + idx2hash = {i: _hash for i, _hash in enumerate(pred_hashes)} + + # Hash paragraphs + paragraphs = [ + paragraph | {"hash": hash(normalize("NFKC", paragraph["plain_text"].strip()))} + for paragraph in paragraphs + ] + + # Assign prediction indices to each paragraph by hash + hash2idx = { + paragraph["hash"]: np.where(np.array(pred_hashes) == paragraph["hash"])[ + 0 + ].tolist() + for paragraph in paragraphs + } + + paragraphs = [ + paragraph | {"pred_indices": hash2idx[paragraph["hash"]]} + for paragraph in paragraphs + ] + + # Identify missing indices + missing_indices = list(set(idx2hash.keys()) - set(flatten(list(hash2idx.values())))) + + if missing_indices: + # Assign prediction indices to each paragraph by lowest CER + target_texts = np.array([prediction["document"] for prediction in predictions])[ + missing_indices + ] + + missing_paragraphs = [ + paragraph for paragraph in paragraphs if not paragraph["pred_indices"] + ] + + for missing_paragraph in missing_paragraphs: + source_text = missing_paragraph["plain_text"] + min_cer_idx = np.argmin( + [cer(source_text, target_text) for target_text in target_texts] + ) + missing_paragraph["pred_indices"] = [missing_indices[min_cer_idx]] + + # Assign document text and labels + paragraphs = [ + paragraph + | { + "document": predictions[paragraph["pred_indices"][0]]["document"], + "labels": predictions[paragraph["pred_indices"][0]]["labels"], + } + for paragraph in paragraphs + ] + + return paragraphs diff --git a/aymurai/text/anonymization/doc_anonymizer.py b/aymurai/text/anonymization/doc_anonymizer.py new file mode 100644 index 00000000..7feb6f3a --- /dev/null +++ b/aymurai/text/anonymization/doc_anonymizer.py @@ -0,0 +1,88 @@ +import os +import tempfile +from glob import glob + +from more_itertools import flatten + +from aymurai.meta.pipeline_interfaces import Transform +from aymurai.text.anonymization.alignment import ( + index_paragraphs, + match_paragraphs_with_predictions, +) +from aymurai.text.anonymization.watermarks import add_footer_watermark +from aymurai.text.anonymization.xml_docx import ( + create_docx, + replace_text_in_xml, + unzip_document, +) +from aymurai.utils.cache import cache_load, cache_save, get_cache_key + + +class DocAnonymizer(Transform): + """ + Anonymize document by replacing sensitive data with label tokens + """ + + def __init__(self, use_cache: bool = False): + self.use_cache = use_cache + self.render_context = None + + def __call__(self, item: dict, preds: list[dict], output_dir: str = ".") -> None: + """ + Performs the anonymization process on a document. + + Args: + item (dict): The document item to be anonymized. + preds (list[dict]): The list of predictions for the document. + output_dir (str, optional): The directory to save the anonymized document. + Defaults to ".". + + Raises: + ValueError: If the document has an extension other than `.docx`. + """ + item_path = item["path"] + + if not os.path.splitext(item_path)[-1] == ".docx": + raise ValueError("Only `.docx` extension is allowed.") + + if not item.get("data"): + item["data"] = {} + + cache_key = get_cache_key(item_path, self.__name__) + if self.use_cache and (cache_data := cache_load(key=cache_key)): + paragraphs = cache_data + else: + # Unzip document into a temporary directory + with tempfile.TemporaryDirectory() as tempdir: + unzip_document(item_path, tempdir) + + # Parse XML files + xml_files = glob(f"{tempdir}/**/*.xml", recursive=True) + paragraphs = (index_paragraphs(file) for file in xml_files) + paragraphs = list(flatten(paragraphs)) + + # Filter out empty paragraphs + paragraphs = [ + paragraph + for paragraph in paragraphs + if paragraph["plain_text"].strip() + ] + + # Matching + paragraphs = match_paragraphs_with_predictions(paragraphs, preds) + + # Edit XML filess + replace_text_in_xml(paragraphs, tempdir, self.render_context) + + # Recreate anonymized document + os.makedirs(output_dir, exist_ok=True) + create_docx( + tempdir, + f"{output_dir}/{os.path.basename(item_path)}", + ) + + # Add watermark to the footer + add_footer_watermark(f"{output_dir}/{os.path.basename(item_path)}") + + if self.use_cache: + cache_save(paragraphs, key=cache_key) diff --git a/aymurai/text/anonymization/watermarks.py b/aymurai/text/anonymization/watermarks.py new file mode 100644 index 00000000..3199bb9e --- /dev/null +++ b/aymurai/text/anonymization/watermarks.py @@ -0,0 +1,197 @@ +from docx import Document +from docx.enum.text import WD_ALIGN_PARAGRAPH +from docx.opc.constants import RELATIONSHIP_TYPE +from docx.oxml.shared import OxmlElement, qn +from docx.shared import Inches, Pt, RGBColor + + +def _add_hyperlink( + paragraph, + text: str, + url: str, + font_name: str = "Archivo", + size: int = 10, + color: RGBColor = RGBColor(115, 190, 250), + italic: bool = False, + bold: bool = True, + underline: bool = True, +) -> None: + """ + Adds a formatted hyperlink to a given paragraph in a Word document. + + Notes: + - This method directly manipulates the underlying XML of the paragraph to insert a hyperlink, + as python-docx does not natively support hyperlinks. + - The hyperlink will be appended to the end of the given paragraph. + - Formatting options (font, size, color, italic, bold, underline) are applied to the hyperlink text. + + Args: + paragraph: The python-docx paragraph object to which the hyperlink will be added. + text (str): The display text for the hyperlink. + url (str): The URL that the hyperlink points to. + font_name (str, optional): The font name to use for the hyperlink text. Defaults to "Archivo". + size (int, optional): The font size (in points) for the hyperlink text. Defaults to 10. + color (RGBColor, optional): The font color as an RGBColor tuple. Defaults to RGBColor(115, 190, 250). + italic (bool, optional): Whether the hyperlink text should be italicized. Defaults to False. + bold (bool, optional): Whether the hyperlink text should be bold. Defaults to True. + underline (bool, optional): Whether the hyperlink text should be underlined. Defaults to True. + + Raises: + ValueError: If the paragraph is not a valid python-docx paragraph object. + """ + # Create the hyperlink relationship + part = paragraph.part + r_id = part.relate_to(url, RELATIONSHIP_TYPE.HYPERLINK, is_external=True) + + # Create the hyperlink element + hyperlink = OxmlElement("w:hyperlink") + hyperlink.set(qn("r:id"), r_id) + + # Create a new run + new_run = OxmlElement("w:r") + + # Set run properties (formatting) + r_pr = OxmlElement("w:rPr") + + # Set font + if font_name: + font = OxmlElement("w:rFonts") + font.set(qn("w:ascii"), font_name) + font.set(qn("w:hAnsi"), font_name) + r_pr.append(font) + + # Set color + if color: + color_el = OxmlElement("w:color") + color_el.set(qn("w:val"), f"{color[0]:02x}{color[1]:02x}{color[2]:02x}") + r_pr.append(color_el) + + # Set italic + if italic: + r_pr.append(OxmlElement("w:i")) + + # Set bold + if bold: + r_pr.append(OxmlElement("w:b")) + + # Set underline - added for links + if underline: + underline_el = OxmlElement("w:u") + underline_el.set(qn("w:val"), "single") # single underline + r_pr.append(underline_el) + + # Set size + sz = OxmlElement("w:sz") + sz.set(qn("w:val"), str(size * 2)) # Word uses half-points + r_pr.append(sz) + + # Add properties to run + new_run.append(r_pr) + + # Set text + text_el = OxmlElement("w:t") + text_el.text = text + new_run.append(text_el) + + # Add run to hyperlink + hyperlink.append(new_run) + + # Add hyperlink to paragraph + paragraph._p.append(hyperlink) + + +def _add_watermark_to_footer( + footer, + alignment, + font_name: str = "Archivo", + hyperlink_text: str = "AymurAI", + hyperlink_url: str = "https://www.aymurai.info/", + watermark_text: str = "Documento anonimizado por AymurAI", +) -> None: + """ + Adds a watermark text to the footer of a document. + + Args: + footer: The footer object to which the watermark will be added. + alignment: The alignment setting for the paragraph (e.g., left, center, right). + font_name (str, optional): The font name to use for the watermark text. Defaults to "Archivo". + hyperlink_text (str, optional): The text to be hyperlinked. Defaults to "AymurAI". + hyperlink_url (str, optional): The URL to link "AymurAI" to. Defaults to "https://www.aymurai.info/". + watermark_text (str): The text to be used as the watermark. Defaults to "Documento anonimizado por AymurAI". + """ + paragraph = footer.add_paragraph() + paragraph.alignment = alignment + + if hyperlink_url and hyperlink_text in watermark_text: + parts = watermark_text.split(hyperlink_text, 1) + before_text = parts[0] + after_text = parts[1] if len(parts) > 1 else "" + + # Add text before the hyperlink + if before_text: + run = paragraph.add_run(before_text) + run.font.name = "Archivo" + run.font.color.rgb = RGBColor(192, 192, 192) + run.font.size = Pt(10) + + # Add hyperlink + _add_hyperlink(paragraph, hyperlink_text, hyperlink_url) + + # Add text after the hyperlink + if after_text: + run = paragraph.add_run(after_text) + run.font.name = font_name + run.font.color.rgb = RGBColor(192, 192, 192) + run.font.size = Pt(10) + + else: + # Just add the full text without a hyperlink + run = paragraph.add_run(watermark_text) + run.font.name = font_name + run.font.color.rgb = RGBColor(192, 192, 192) + run.font.size = Pt(10) + + +def add_footer_watermark( + doc_path: str, + font_name: str = "Archivo", + hyperlink_text: str = "AymurAI", + hyperlink_url: str = "https://www.aymurai.info/", + watermark_text: str = "Documento anonimizado por AymurAI", +) -> None: + """ + Adds a watermark to the footer of each section in a Word document. + + Args: + doc_path (str): Path to the document. + font_name (str, optional): The font name to use for the watermark text. Defaults to "Archivo". + hyperlink_text (str, optional): The text to be hyperlinked. Defaults to "AymurAI". + hyperlink_url (str, optional): The URL to link "AymurAI" to. Defaults to "https://www.aymurai.info/". + watermark_text (str, optional): The text to be used as the watermark. Defaults to "Documento anonimizado por AymurAI". + """ + document = Document(doc_path) + processed_footers = set() + + for section in document.sections: + section.footer_distance = Inches(0.1) + + # List of (footer_obj, alignment) tuples + footers = [(section.footer, WD_ALIGN_PARAGRAPH.RIGHT)] # Odd/default + if section.even_page_footer is not None: + footers.append((section.even_page_footer, WD_ALIGN_PARAGRAPH.LEFT)) + if section.different_first_page_header_footer: + footers.append((section.first_page_footer, WD_ALIGN_PARAGRAPH.RIGHT)) + + for footer, alignment in footers: + if id(footer) not in processed_footers: + _add_watermark_to_footer( + footer, + alignment, + font_name=font_name, + hyperlink_text=hyperlink_text, + hyperlink_url=hyperlink_url, + watermark_text=watermark_text, + ) + processed_footers.add(id(footer)) + + document.save(doc_path) diff --git a/aymurai/text/anonymization/xml_docx.py b/aymurai/text/anonymization/xml_docx.py new file mode 100644 index 00000000..1ae9f50e --- /dev/null +++ b/aymurai/text/anonymization/xml_docx.py @@ -0,0 +1,172 @@ +import os +import re +import xml.sax.saxutils +import zipfile + +import pandas as pd +from lxml import etree + +from aymurai.text.anonymization.alignment import parse_token_indices + + +def unzip_document(doc_path: str, output_dir: str) -> None: + """ + Unzips the document file to the specified output directory. + + Args: + doc_path (str): The path to the document file. + output_dir (str): The directory where the contents of the document + file will be extracted. + """ + # Ensure the output directory exists + os.makedirs(output_dir, exist_ok=True) + + # Open the doc file as a zip file + with zipfile.ZipFile(doc_path, "r") as doc_zip: + # Extract all the contents to the output directory + doc_zip.extractall(output_dir) + + +def normalize_document(xml_content: str) -> str: + """ + Normalizes the XML document by removing extra spaces, preserving line breaks, + and removing hyperlinks while preserving text content. + + Args: + xml_content (str): The XML content to be normalized. + + Returns: + str: The normalized XML content. + """ + # Parse the XML content + parser = etree.XMLParser(ns_clean=True) + root = etree.fromstring(xml_content.encode("utf-8"), parser) + + # Extract namespaces + namespaces = {k: v for k, v in root.nsmap.items() if k} + + # Remove hyperlinks but preserve the text content + for hyperlink in root.xpath("//w:hyperlink", namespaces=namespaces): + parent = hyperlink.getparent() + index = list(parent).index(hyperlink) + + # Move all text-containing children (e.g., w:r elements) outside the hyperlink tag + for child in hyperlink: + parent.insert(index, child) + index += 1 + parent.remove(hyperlink) # Remove the element itself + + # Process each paragraph + for wp in root.xpath("//w:p", namespaces=namespaces): + first = True + + # Find all w:r elements containing w:t elements + for wr in wp.xpath(".//w:r", namespaces=namespaces): + wt = wr.find(".//w:t", namespaces) + if wt is not None and wt.text: + # Normalize spaces within the text, preserving new line breaks + wt.text = re.sub(r"[^\S\r\n]+", " ", wt.text) + + # Add a leading space if not the first fragment in the paragraph + if not first: + wt.text = " " + wt.text.lstrip() + else: + wt.text = wt.text.lstrip() + first = False + + # Remove trailing spaces from all fragments + wt.text = wt.text.rstrip() + + # Set the xml:space attribute to preserve + wt.set( + "{http://www.w3.org/XML/1998/namespace}space", + "preserve", + ) + + # Check if the text is empty after normalization + if not wt.text or wt.text.strip() == "": + # Remove the w:r element from its parent + wr.getparent().remove(wr) + + # Write back the XML content to a string + xml_str = etree.tostring(root, encoding="unicode", pretty_print=True) + + return xml_str + + +def replace_text_in_xml( + paragraphs: list[dict], base_dir: str, render_context: dict | None = None +) -> None: + """ + Replaces text in XML files based on the provided paragraphs + and saves the modified files. + + Args: + paragraphs (list[dict]): A list of dictionaries representing + the paragraphs to be replaced. + base_dir (str): The base directory where the XML files are located. + render_context (dict | None, optional): The context for rendering labels. Defaults to None. + """ + tokens = pd.concat( + [parse_token_indices(sample, render_context) for sample in paragraphs], + ignore_index=True, + ) + + fragments = ( + tokens.groupby(["xml_file", "paragraph_index", "fragment_index"]) + .agg({"target": " ".join, "start_char": "min", "end_char": "max"}) + .reset_index() + ) + + for xml_file, group in fragments.groupby("xml_file"): + group = group.sort_values("end_char", ascending=False) + + with open(f"{base_dir}/word/{xml_file}", "r+") as file: + content = file.read() + + for _, r in group.iterrows(): + start_char = r["start_char"] + end_char = r["end_char"] + + target = r["target"] + target = re.sub(r"[^\S\r\n]+", " ", target) + + # Escape XML special characters + target = xml.sax.saxutils.escape(target) + + content = content[:start_char] + target + content[end_char:] + + # MUST be at the end to dont screw up the indexes + content = normalize_document(content) + + file.seek(0) + file.write(content) + file.truncate() + + +def add_files_to_zip(zip_file: zipfile.ZipFile, directory: str) -> None: + """ + Adds all files in the specified directory to a zip file. + + Args: + zip_file (zipfile.ZipFile): The zip file to add the files to. + directory (str): The directory containing the files to be added. + """ + for root, _, files in os.walk(directory): + for file in files: + file_path = os.path.join(root, file) + zip_file.write(file_path, os.path.relpath(file_path, directory)) + + +def create_docx(xml_directory: str, output_file: str) -> None: + """ + Creates a new DOCX file by adding XML components from the specified directory. + + Args: + xml_directory (str): The directory containing the XML components. + output_file (str): The path to the output DOCX file. + """ + # Create a new zip file + with zipfile.ZipFile(output_file, "w") as docx: + # Add XML components + add_files_to_zip(docx, xml_directory) diff --git a/aymurai/transforms/datetime_formatter/core.py b/aymurai/transforms/datetime_formatter/core.py index fbdab7e1..e98c0d76 100644 --- a/aymurai/transforms/datetime_formatter/core.py +++ b/aymurai/transforms/datetime_formatter/core.py @@ -2,9 +2,9 @@ from datetime_matcher import DatetimeMatcher +from aymurai.meta.pipeline_interfaces import Transform from aymurai.meta.types import DataItem from aymurai.utils.misc import get_element -from aymurai.meta.pipeline_interfaces import Transform from .patterns import patterns diff --git a/docs/anonymization/README.md b/docs/anonymization/README.md index 899a2081..9beee13d 100644 --- a/docs/anonymization/README.md +++ b/docs/anonymization/README.md @@ -10,13 +10,64 @@ 1. `/misc/document-extract` recibe el DOCX/PDF y devuelve el payload estructurado (`document_id`, lista de párrafos) tal como hoy. 2. Se procesa cada párrafo con `/anonymizer/predict`, obteniendo los resultados de NER (`aymurai_label`, texto, offsets, contexto previo y posterior y otros atributos). 3. Se consolida la inferencia: agrupamos todas las menciones detectadas y armamos un paquete enriquecido con contexto circundante (párrafo u oraciones vecinas). -4. Módulo de fuzzy-matching cruza cada mención extraida para sugerir candidatos de `CanonicalEntities` (`canonical_text` y `aliases`). +4. Módulo de fuzzy-matching cruza cada mención extraída para sugerir candidatos de `CanonicalEntities` (`canonical_text` y `aliases`). 5. El contexto del paso 3 más las sugerencias del paso 4 son insumo para el servicio de desambiguación (LLM); el modelo se encarga de validar los `CanonicalEntities` sugeridos o proponer nuevos, al tiempo que infiere atributos adicionales (rol procesal), devolviendo una lista refinada de `CanonicalEntities`. -6. Con esa lista se ejecuta el mapeo de `Entity` a `CanonicalEntity`: cada mención NER queda anotada con un `canonical_entity_id` y un `aymurai_label_suffix` y cualquier atributo inferido, generando la desambiguación de entidades. +6. Con esa lista se ejecuta el mapeo de `Entity` a `CanonicalEntity`: cada mención NER queda anotada con un `canonical_entity_id` y un `aymurai_label_instance` (índice entero por orden de aparición) y cualquier atributo inferido, generando la desambiguación de entidades. 7. El frontend consume esos outputs y muestra el texto original con dos niveles de revisión: etiquetas NER y asignación canónica. -8. Tras la validación manual, el cliente invoca `/anonymizer/anonymize-document`, que ahora utiliza el `aymurai_label_suffix` para aplicar reemplazos consistentes y generar el documento anonimizado final. +8. Tras la validación manual, el cliente invoca `/anonymizer/anonymize-document`, que ahora utiliza políticas de render (`render_policy`) para aplicar reemplazos consistentes (etiqueta o subclase con sufijos opcionales) y generar el documento anonimizado final. 9. El documento anonimizado queda disponible para descarga y se registran en la base de datos las decisiones finales (etiquetas y canónicos). +## Cambios principales en el modelo de datos + +### Nuevos campos en `EntityAttributes` + +- `canonical_entity_id`: UUID de la entidad canónica asignada a cada mención. +- `aymurai_label_instance`: índice entero por orden de aparición del `canonical_entity_id` dentro de un mismo label (1, 2, 3...). +- `aymurai_label_subclass`: lista de roles inferidos por LLM (p. ej. "Denunciante", "Juez/a"). +- `aymurai_disambiguation`: método aplicado para la desambiguación efectiva del label (`llm`, `fuzzy`, `none`). +- `aymurai_anonymize`: flag efectivo de anonimización (True/False). + +### Políticas por label (nueva interfaz) + +Se incorpora un esquema de políticas por etiqueta que permite decidir: + +- `anonymize`: `true | false` +- `disambiguation`: `"none" | "fuzzy" | "llm"` +- `use_subclass_when_available`: `true | false` + +Estas políticas pueden venir de: + +1. Configuración del servidor (variable de entorno `DISAMBIGUATION_LABEL_POLICIES`). +2. Request del usuario (campo `label_policies` en `/anonymizer/disambiguate` y `/anonymizer/anonymize-document`). + +## Render Policy (integración con frontend) + +Para evitar sumar flags adicionales en cada entidad, el comportamiento de render se controla con un objeto `render_policy` (a nivel request/documento), que define: + +- `suffix_mode`: `"auto" | "always" | "never"`. +- `suffix_threshold`: umbral para agregar sufijo en modo `auto`. + +Esto permite generar tokens como: + +- `` si hay un solo juez. +- ``, `` si hay múltiples. +- `` si `use_subclass_when_available` es `false` ó `use_subclass_when_available` es `true` pero no hay subclase disponible para esa entidad. + +El `render_policy` puede definirse por entorno (`RENDER_POLICY`) o por request y se aplica al momento de exportar el documento con `/anonymizer/anonymize-document`. + +## Cambios en la API + +### `/anonymizer/disambiguate` + +- **Nuevo input opcional**: `label_policies`. +- **Nuevo output**: `label_policies` y metadatos efectivos en cada `DocLabel` (`aymurai_disambiguation`, `aymurai_anonymize`). +- La selección de labels para LLM/fuzzy se define por políticas, no por un modo global. + +### `/anonymizer/anonymize-document` + +- **Input**: `DocumentAnnotations` incluye `label_policies` y `render_policy`. +- **Render**: los tokens se generan según `render_policy` y respetan `aymurai_anonymize`. + ## Plan de evaluación incremental El objetivo general es optimizar la extracción y desambiguación de entidades. Para avanzar con criterio orientado a métricas mediremos cada capa y el sistema end-to-end. @@ -29,5 +80,5 @@ Finalmente consolidaremos una evaluación integrada combinando el mejor NER disp ## Consideraciones de integración con frontend -- Necesitamos habilitar una lista configurable de entidades a excluir de la anonimización. El frontend deberá permitir que la persona usuaria decida, por ejemplo, mantener visibles o no entidades específicas o menciones a funcionariaos públicos según su rol procesal. -- Las exclusiones y la edición manual posterior impactan directamente en el ordenamiento de los `aymurai_label_suffix`. Habrá que recalcular los sufijos luego de aplicar exclusiones y validaciones para evitar huecos o inconsistencias entre el texto mostrado y los reemplazos finales. +- Necesitamos habilitar una lista configurable de entidades a excluir de la anonimización. El frontend deberá permitir que la persona usuaria decida, por ejemplo, mantener visibles o no entidades específicas o menciones a funcionarios públicos según su rol procesal. +- Las exclusiones y la edición manual posterior impactan directamente en el ordenamiento de los `aymurai_label_instance`. Habrá que recalcular los sufijos luego de aplicar exclusiones y validaciones para evitar huecos o inconsistencias entre el texto mostrado y los reemplazos finales. diff --git a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb index 8ad00ae4..6eed83f1 100644 --- a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb +++ b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb @@ -230,7 +230,6 @@ " call_extraction_api as extract_document,\n", ")\n", "\n", - "\n", "def discover_documents(root: Path, extensions: set[str]) -> list[Path]:\n", " extensions = {ext.lower() for ext in extensions}\n", " return sorted(\n", @@ -239,7 +238,6 @@ " if path.is_file() and path.suffix.lower() in extensions\n", " )\n", "\n", - "\n", "def predict_paragraphs(paragraphs: list[str]) -> list[dict]:\n", " endpoint = f\"{API_BASE_URL}/anonymizer/predict\"\n", " predictions = []\n", diff --git a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb new file mode 100644 index 00000000..1b3566f9 --- /dev/null +++ b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb @@ -0,0 +1,266 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "from pathlib import Path\n", + "\n", + "import requests\n", + "from tqdm import tqdm\n", + "\n", + "from aymurai.experiments.entity_disambiguation.runner import (\n", + " call_extraction_api as extract_document,\n", + ")\n", + "\n", + "API_URL = \"http://localhost:8000\" # Url for debugger. change it to your own\n", + "DATA_ROOT = Path(\n", + " os.getenv(\n", + " \"DISAMBIGUATION_DATA_ROOT\",\n", + " \"../../../resources/data/restricted/disambiguation-eval/files\",\n", + " )\n", + ")\n", + "DOC_EXTENSIONS = {\".pdf\", \".docx\"}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sample document\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def discover_documents(root: Path, extensions: set[str]) -> list[Path]:\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", + "\n", + "print(f\"Found {len(documents)} documents\")\n", + "\n", + "doc_path = documents[16]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## /document-extract endpoint output\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# /document-extract endpoint output\n", + "session = requests.Session()\n", + "document = extract_document(\n", + " session,\n", + " endpoint=f\"{API_URL}/misc/document-extract\",\n", + " file_path=doc_path,\n", + " timeout_s=300,\n", + ")\n", + "paragraphs = document[\"detail\"][\"document\"]\n", + "document" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(paragraphs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Inference\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Function to make inference using the API\n", + "def get_predictions(sample: str) -> dict:\n", + " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample}, params={\"use_cache\": False})\n", + " response.raise_for_status()\n", + " return response.json()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "predictions = [get_predictions(paragraph) for paragraph in tqdm(paragraphs)]\n", + "predictions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Export variants\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def disambiguate_and_export(\n", + " variant_name: str, label_policies: dict, render_policy: dict\n", + "):\n", + " response = requests.post(\n", + " url=f\"{API_URL}/anonymizer/disambiguate\",\n", + " json={\n", + " \"paragraphs\": predictions,\n", + " # \"custom_prompts\": {\"root\": []},\n", + " \"label_policies\": label_policies,\n", + " },\n", + " )\n", + " response.raise_for_status()\n", + " disambiguated = response.json()\n", + "\n", + " json_prediction = json.dumps(\n", + " {\n", + " \"data\": disambiguated[\"data\"],\n", + " \"label_policies\": label_policies,\n", + " \"render_policy\": render_policy,\n", + " }\n", + " )\n", + "\n", + " with open(doc_path, \"rb\") as file:\n", + " files = {\"file\": file}\n", + "\n", + " response = requests.post(\n", + " url=f\"{API_URL}/anonymizer/anonymize-document\",\n", + " data={\"annotations\": json_prediction},\n", + " files=files,\n", + " )\n", + " response.raise_for_status()\n", + "\n", + " output_dir = \"output\"\n", + " os.makedirs(output_dir, exist_ok=True)\n", + "\n", + " filename = os.path.basename(doc_path)\n", + " filename, ext = os.path.splitext(filename)\n", + " out_path = f\"{output_dir}/{filename}-{variant_name}.odt\"\n", + "\n", + " with open(out_path, \"wb\") as file:\n", + " file.write(response.content)\n", + "\n", + " return out_path" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "render_policy = {\n", + " \"suffix_mode\": \"auto\",\n", + " \"suffix_threshold\": 1,\n", + "}\n", + "\n", + "# 1) everything fuzzy\n", + "label_policies_all_fuzzy = {\n", + " \"PER\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": True},\n", + " \"DNI\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"LOC\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"FECHA\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + "}\n", + "out_all_fuzzy = disambiguate_and_export(\n", + " \"all-fuzzy\", label_policies_all_fuzzy, render_policy\n", + ")\n", + "\n", + "# 2) FECHAs excluded\n", + "label_policies_no_fecha = {\n", + " \"PER\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": True},\n", + " \"DNI\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"LOC\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"FECHA\": {\"disambiguation\": \"none\", \"anonymize\": False, \"use_subclass_when_available\": False},\n", + "}\n", + "out_no_fecha = disambiguate_and_export(\n", + " \"no-fecha\", label_policies_no_fecha, render_policy\n", + ")\n", + "\n", + "# 3) PER llm-disambiguated\n", + "label_policies_per_llm = {\n", + " \"PER\": {\"disambiguation\": \"llm\", \"anonymize\": False, \"use_subclass_when_available\": True},\n", + " \"DNI\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"LOC\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"FECHA\": {\"disambiguation\": \"none\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + "}\n", + "out_per_llm = disambiguate_and_export(\"per-llm\", label_policies_per_llm, render_policy)\n", + "\n", + "out_all_fuzzy, out_no_fecha, out_per_llm" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 96a88ef2fafbb386331f29c18014ef7dbece88f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= Date: Thu, 30 Jan 2025 21:54:41 +0000 Subject: [PATCH 035/101] =?UTF-8?q?=E2=8F=AA=20Revert=20entrypoint.sh=20to?= =?UTF-8?q?=201ac2776?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .devcontainer/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/entrypoint.sh b/.devcontainer/entrypoint.sh index 214ac9f6..000c3360 100644 --- a/.devcontainer/entrypoint.sh +++ b/.devcontainer/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh # install dependencies -uv sync --frozen --all-extras --group mlops +uv sync --frozen --all-extras # configure precommit uv run pre-commit install From 20f9e9236803c1d0b8e5c0a7697fbacee2dc9cc4 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 18 Feb 2026 18:31:48 -0300 Subject: [PATCH 036/101] =?UTF-8?q?=E2=8F=AA=20Revert=20.dockerignore=20to?= =?UTF-8?q?=205af5814?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index e84e738a..9b91c7d3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,6 @@ resources/ !resources/api !resources/pipelines -!resources/llm !resources/cache/aymurai !resources/cache/tfhub_modules notebooks/ From 04f33432193adeb5c9f2ff1aabfb5449c06dfe56 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 18 Feb 2026 18:50:35 -0300 Subject: [PATCH 037/101] =?UTF-8?q?=E2=8F=AA=20Revert=20.env.common=20to?= =?UTF-8?q?=2090f7369?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.common | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.env.common b/.env.common index 25d41bb8..1eec416e 100644 --- a/.env.common +++ b/.env.common @@ -8,12 +8,3 @@ TESSDATA_PREFIX=/usr/local/share/tessdata AYMURAI_RESTRICTED_DOCUMENT_PDFS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO-pdf" AYMURAI_RESTRICTED_DOCUMENT_DOCS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO" - -API_BASE_URL=http://localhost:8899 -REQUEST_TIMEOUT=300 - -MLFLOW_TRACKING_URI=http://localhost:5000 -MLFLOW_S3_ENDPOINT_URL=http://localhost:9002 -MLFLOW_S3_IGNORE_TLS=true -MLFLOW_EXPERIMENT_NAME=entity-disambiguation -MLFLOW_ARTIFACT_ROOT=s3://mlflow/artifacts \ No newline at end of file From 1dbd24c303cb04542c395b0f8c0357cf82816e87 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 18 Feb 2026 19:29:09 -0300 Subject: [PATCH 038/101] =?UTF-8?q?=E2=8F=AA=20Revert=20.vscode/launch.jso?= =?UTF-8?q?n=20to=20f366690?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/launch.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d6348f6f..00fd92d6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,8 +19,7 @@ ], "subProcess": false, "envFile": "${workspaceFolder}/.env", - "python": "${workspaceFolder}/.venv/bin/python", - "preLaunchTask": "Start Ollama service" + "python": "${workspaceFolder}/.venv/bin/python" }, ] } \ No newline at end of file From f645881a3482a6e2f1ab6fde13bfc89a22dfd883 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 18 Feb 2026 19:36:53 -0300 Subject: [PATCH 039/101] =?UTF-8?q?=E2=8F=AA=20Revert=20Makefile=20to=20cb?= =?UTF-8?q?3df05?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 124 ++++--------------------------------------------------- 1 file changed, 7 insertions(+), 117 deletions(-) diff --git a/Makefile b/Makefile index b49053fd..0f132348 100644 --- a/Makefile +++ b/Makefile @@ -3,131 +3,21 @@ export $(shell sed 's/=.*//' .env) include .env.common export $(shell sed 's/=.*//' .env.common) -# Select which Ollama service to control (ollama or ollama-gpu) -OLLAMA_SERVICE ?= ollama -OLLAMA_CONTAINER := $(shell docker ps -q -f name=^$(OLLAMA_SERVICE)$$) -# Select which API service to control (override with API_SERVICE=aymurai-api-gpu) -API_SERVICE ?= aymurai-api -# Select which full API service to control (override with API_FULL_SERVICE=aymurai-api-full-gpu) -API_FULL_SERVICE ?= aymurai-api-full api-build: - docker compose build $(API_SERVICE) + docker compose build aymurai-api-dev api-run: - @if [ "$(API_SERVICE)" = "aymurai-api-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose run --service-ports $(API_SERVICE) -api-up: - @if [ "$(API_SERVICE)" = "aymurai-api-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose up -d $(API_SERVICE) -api-stop: - docker compose stop $(API_SERVICE) -api-logs: - docker compose logs -f $(API_SERVICE) + docker compose run --service-ports aymurai-api-dev api-pull: - docker compose pull $(API_SERVICE) + docker compose pull aymurai-api api-full-build: - docker compose build $(API_FULL_SERVICE) + docker compose build aymurai-api-full api-full-run: - @if [ "$(API_FULL_SERVICE)" = "aymurai-api-full-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose run --service-ports $(API_FULL_SERVICE) -api-full-up: - @if [ "$(API_FULL_SERVICE)" = "aymurai-api-full-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose up -d $(API_FULL_SERVICE) -api-full-stop: - docker compose stop $(API_FULL_SERVICE) -api-full-logs: - docker compose logs -f $(API_FULL_SERVICE) + docker compose run aymurai-api-full api-full-pull: - docker compose pull $(API_FULL_SERVICE) - -ollama-up: - @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose up -d --no-recreate $(OLLAMA_SERVICE) - -ollama-stop: - docker compose stop $(OLLAMA_SERVICE) - -ollama-restart: - docker compose restart $(OLLAMA_SERVICE) - -ollama-pull: -ifndef MODEL - $(error MODEL variable is required, e.g. make ollama-pull MODEL=llama3) -endif -ifndef OLLAMA_CONTAINER - $(error Ollama container '$(OLLAMA_SERVICE)' is not running. Start it first with 'make ollama-up OLLAMA_SERVICE=$(OLLAMA_SERVICE)') -endif - docker exec $(OLLAMA_SERVICE) ollama pull $(MODEL) - -ollama-run: -ifndef MODEL - $(error MODEL variable is required, e.g. make ollama-run MODEL=llama3) -endif - @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose up -d --no-recreate $(OLLAMA_SERVICE) - docker compose exec -it $(OLLAMA_SERVICE) ollama run $(MODEL) - -ollama-list: - @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose up -d --no-recreate $(OLLAMA_SERVICE) - docker compose exec $(OLLAMA_SERVICE) ollama list - -ollama-rm: -ifndef MODEL - $(error MODEL variable is required, e.g. make ollama-rm MODEL=llama3) -endif - @if [ "$(OLLAMA_SERVICE)" = "ollama-gpu" ]; then \ - docker compose stop ollama || true; \ - else \ - docker compose stop ollama-gpu || true; \ - fi - docker compose up -d --no-recreate $(OLLAMA_SERVICE) - docker compose exec $(OLLAMA_SERVICE) ollama rm $(MODEL) - -mlops-up: - docker compose up -d postgres minio minio-mc mlflow - -mlops-stop: - docker compose stop mlflow minio minio-mc postgres - -mlops-logs: - docker compose logs -f mlflow - -exp-run: -ifndef CONFIG - $(error CONFIG variable is required, e.g. make exp-run CONFIG=resources/experiments/entity-disambiguation/exp-gemma3-pv1.yaml) -endif - uv run --group mlops -- python -m aymurai.experiments.entity_disambiguation.runner --config $(CONFIG) + docker compose pull aymurai-api-full stress-test: locust -f locustfile.py --host http://localhost:8899 @@ -137,4 +27,4 @@ alembic-regenerate: rm -rvf aymurai/database/versions/* && \ cd aymurai && \ uv run alembic revision --autogenerate -m "Create database" && \ - uv run alembic upgrade head + uv run alembic upgrade head \ No newline at end of file From 8983156a02de1100ff91f662868c69a83d40d154 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 18 Feb 2026 19:40:09 -0300 Subject: [PATCH 040/101] =?UTF-8?q?=E2=8F=AA=20Revert=20aymurai/api.core.p?= =?UTF-8?q?y=20to=2019a9ca8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/api/core.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/aymurai/api/core.py b/aymurai/api/core.py index fe955b4b..c9223455 100644 --- a/aymurai/api/core.py +++ b/aymurai/api/core.py @@ -3,7 +3,6 @@ from .endpoints.routers.anonymizer import anonymizer from .endpoints.routers.anonymizer import database as anonymizer_database from .endpoints.routers.datapublic import datapublic -from .endpoints.routers.llm import summarization from .endpoints.routers.misc import convert, document_extract from .endpoints.routers.server import stats @@ -37,13 +36,6 @@ tags=["datapublic/model"], ) -# LLM -router.include_router( - summarization.router, - prefix="/llm", - tags=["llm/summarization"], -) - # Misc router.include_router(document_extract.router, tags=["document"], deprecated=True) From 8ca5420062b0a1cfd14cfac6b7bec4e77b506f7c Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 18 Feb 2026 19:59:52 -0300 Subject: [PATCH 041/101] =?UTF-8?q?=F0=9F=A6=96=20Changed=20aymurai/api/en?= =?UTF-8?q?dpoints/routers/anonymizer/anonymizer.py=20for=20release/v1.5.0?= =?UTF-8?q?=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routers/anonymizer/anonymizer.py | 63 +------------------ 1 file changed, 3 insertions(+), 60 deletions(-) diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 9d963ab1..5b3690bd 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -37,7 +37,6 @@ from aymurai.utils.entity_disambiguation import ( build_canonical_entities, get_canonical_dates, - llm_canonical_entities_inference, load_prompts_from_yaml, map_canonical_entities_ner_preds, ) @@ -466,70 +465,14 @@ async def anonymizer_disambiguate( "fuzzy clustering produced %d canonical entities", len(canonical_entities) ) - # --- LLM REFINEMENT (policy-driven) --- - canonical_entities_llm = [] - - for label in llm_labels: - logger.info("llm refinement: label=%s", label) - prompt_set = custom_prompts.get(label) or prompt_library.get(label) - - if ( - not prompt_set - or not prompt_set.system.strip() - or not prompt_set.user.strip() - ): - logger.info("llm refinement skipped: missing prompt for label=%s", label) - continue - - entities_for_this_label = [ - e for e in canonical_entities if e.aymurai_label == label - ] - - if not entities_for_this_label: - logger.info("llm refinement skipped: no entities for label=%s", label) - continue - logger.info( - "llm refinement input: label=%s entities=%d", - label, - len(entities_for_this_label), - ) - - llm_response = llm_canonical_entities_inference( - paragraphs=paragraphs, - canonical_entities_pre_cluster=entities_for_this_label, - system_prompt=prompt_set.system, - user_prompt_template=prompt_set.user, - model=settings.MODEL, - context_window_length=settings.CONTEXT_WINDOW_LENGTH, - model_context=settings.MODEL_CONTEXT, - token_limit_frac=settings.TOKEN_LIMIT_FRAC, - tokenizer_model=settings.TOKENIZER_MODEL, - target_label=label, - temperature=settings.TEMPERATURE, - decompose_by=settings.DECOMPOSE_BY, - ) - - if llm_response: - canonical_entities_llm.extend(llm_response) - logger.info( - "llm refinement output: label=%s entities=%d", - label, - len(llm_response), - ) - - canonical_entities_merged = canonical_entities_llm + [ - ce for ce in canonical_entities if ce.aymurai_label not in set(llm_labels) - ] logger.info( - "disambiguation merge: llm=%d fuzzy_only=%d total=%d", - len(canonical_entities_llm), - len(canonical_entities_merged) - len(canonical_entities_llm), - len(canonical_entities_merged), + "disambiguation merge: total=%d", + len(canonical_entities), ) predictions = map_canonical_entities_ner_preds( predictions=paragraphs, - canonical_entities=canonical_entities_merged, + canonical_entities=canonical_entities, force_labels=set(llm_labels), ) From 6933321ee723b274bb7643f67f1136d2182b1591 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 18 Feb 2026 20:09:55 -0300 Subject: [PATCH 042/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20aymurai/api/en?= =?UTF-8?q?dpoints/routers/llm=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/api/endpoints/routers/llm/__init__.py | 1 - .../endpoints/routers/llm/summarization.py | 580 ------------------ 2 files changed, 581 deletions(-) delete mode 100644 aymurai/api/endpoints/routers/llm/__init__.py delete mode 100644 aymurai/api/endpoints/routers/llm/summarization.py diff --git a/aymurai/api/endpoints/routers/llm/__init__.py b/aymurai/api/endpoints/routers/llm/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/aymurai/api/endpoints/routers/llm/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/aymurai/api/endpoints/routers/llm/summarization.py b/aymurai/api/endpoints/routers/llm/summarization.py deleted file mode 100644 index a681f594..00000000 --- a/aymurai/api/endpoints/routers/llm/summarization.py +++ /dev/null @@ -1,580 +0,0 @@ -from __future__ import annotations - -import json -from pathlib import Path -from typing import Any, AsyncIterator - -import tiktoken -from fastapi import APIRouter, HTTPException -from fastapi.responses import StreamingResponse -from pydantic import BaseModel, ConfigDict, Field - -from aymurai.llm_providers import OllamaLLMProvider -from aymurai.logger import get_logger -from aymurai.settings import settings -from aymurai.utils.yaml_data import load_yaml - -logger = get_logger(__name__) - -router = APIRouter() - -DEFAULT_MODEL = "gpt-oss:20b" -DEFAULT_OPTIONS = {"num_ctx": 16_384, "num_predict": 4096} -DEFAULT_TOKENIZER = "o200k_harmony" -PROMPT_TEMPLATE_PATH = Path(settings.RESOURCES_BASEPATH) / "llm" / "summarization.yml" - - -def _load_prompt_defaults(path: Path) -> tuple[str, str]: - """ - Load default placeholders from the YAML template, - ensuring both required fields are present. - - Args: - path: Path to the summarization YAML file. - - Returns: - tuple[str, str]: Defaults for `information_to_extract` and `entities_to_identify`. - - Raises: - FileNotFoundError: If the YAML file cannot be located. - RuntimeError: If the defaults block is missing or incomplete. - """ - content = load_yaml(str(path)) - defaults = content.get("defaults", {}) - info = (defaults.get("information-to-extract") or "").strip() - entities = (defaults.get("entities-to-identify") or "").strip() - if not info or not entities: - raise RuntimeError("summarization defaults missing in YAML") - return info, entities - - -DEFAULT_INFORMATION_TO_EXTRACT, DEFAULT_ENTITIES_TO_IDENTIFY = _load_prompt_defaults( - PROMPT_TEMPLATE_PATH -) - - -class SummarizationRequest(BaseModel): - model_config = ConfigDict( - json_schema_extra={ - "example": { - "text": "Texto de entrada a resumir.", - "model": DEFAULT_MODEL, - "tokenizer": DEFAULT_TOKENIZER, - "options": DEFAULT_OPTIONS, - } - } - ) - - text: str = Field(..., description="Raw document to summarize.") - model: str = Field( - default=DEFAULT_MODEL, - description="Model name for Ollama. Defaults to gpt-oss:20b.", - ) - tokenizer: str | None = Field( - default=DEFAULT_TOKENIZER, - description="Tokenizer name (tiktoken). Falls back to whitespace if unavailable.", - ) - system_prompt: str | None = Field( - default=None, - description="Optional system prompt override. Defaults to the template in /resources/llm/summarization.yml.", - ) - information_to_extract: str | None = Field( - default=DEFAULT_INFORMATION_TO_EXTRACT, - description="Template variable information_to_extract.", - ) - entities_to_identify: str | None = Field( - default=DEFAULT_ENTITIES_TO_IDENTIFY, - description="Template variable entities_to_identify.", - ) - options: dict[str, Any] = Field( - default_factory=lambda: DEFAULT_OPTIONS.copy(), - description="Ollama chat options. Defaults to num_ctx=16_384 and num_predict=4096.", - ) - - -class SummarizationStep(BaseModel): - chunk_index: int - input_tokens: int - source: str - - -class SummarizationResponse(BaseModel): - summary: str - model: str - system_prompt: str - options: dict[str, Any] - chunks_used: int - steps: list[SummarizationStep] - - -def _load_template_prompt( - *, - information_to_extract: str | None, - entities_to_identify: str | None, -) -> str: - """ - Load and format the summarization prompt template. - - Args: - information_to_extract: Template placeholder describing what to extract. - entities_to_identify: Template placeholder listing entities to detect. - - Returns: - str: Formatted system prompt for summarization. - - Raises: - HTTPException: If the template file is missing or cannot be parsed. - """ - path = PROMPT_TEMPLATE_PATH - try: - content = load_yaml(str(path)) - template = content["system-prompts"]["template"] - - except FileNotFoundError as exc: - logger.exception(f"Summarization prompt template not found at {path}") - raise HTTPException( - status_code=500, - detail="Summarization prompt template not found.", - ) from exc - except Exception as exc: - logger.exception("Error loading summarization prompt template.") - raise HTTPException( - status_code=500, - detail="Unable to load summarization prompt template.", - ) from exc - - info = information_to_extract or DEFAULT_INFORMATION_TO_EXTRACT - entities = entities_to_identify or DEFAULT_ENTITIES_TO_IDENTIFY - - return template.format( - information_to_extract=info, - entities_to_identify=entities, - ) - - -def _load_tokenizer(tokenizer_name: str | None) -> tiktoken.Encoding | None: - """ - Load a tiktoken tokenizer by name. - - Args: - tokenizer_name (str | None): Name of the tokenizer to load. - - Returns: - tiktoken.Encoding | None: The loaded tokenizer encoding or None if unavailable. - """ - if not tokenizer_name: - return None - - try: - return tiktoken.get_encoding(tokenizer_name) - - except Exception: - logger.warning( - f"Tokenizer {tokenizer_name} is not available. Falling back to whitespace." - ) - return None - - -def _count_tokens(text: str, tokenizer: Any) -> int: - """ - Count the number of tokens in the given text using the provided tokenizer. - - Args: - text (str): The input text to count tokens for. - tokenizer (Any): The tokenizer to use for counting tokens. - - Returns: - int: The number of tokens in the input text. - """ - if tokenizer is None: - return len(text.split()) - try: - return len(tokenizer.encode(text)) - except Exception: - return len(text.split()) - - -def _build_provider( - model_name: str, - *, - tokenizer_name: str | None, - system_prompt: str, - context_limit: int, -) -> OllamaLLMProvider: - """ - Build an Ollama provider, adjusting available context for the system prompt. - - Args: - model_name: Name of the model to use. - tokenizer_name: Tokenizer name to attempt loading. - system_prompt: System prompt to set and budget for. - context_limit: Maximum number of context tokens. - - Returns: - OllamaLLMProvider: Configured OllamaLLMProvider instance. - """ - encoding = _load_tokenizer(tokenizer_name) - available_context = max(context_limit - _count_tokens(system_prompt, encoding), 1) - - return OllamaLLMProvider( - model=model_name, - tokenizer=encoding, - system_prompt=system_prompt, - max_context_tokens=available_context, - ) - - -async def _summarize_once( - provider: OllamaLLMProvider, - *, - text: str, - options: dict[str, Any], -) -> str: - """ - Run a single summarization call on the provider. - - Args: - provider: Initialized OllamaLLMProvider instance. - text: Chunk or combined text to summarize. - options: Provider options forwarded to the model. - - Returns: - str: The model's text output for the given input. - """ - response = await provider.async_generate(prompt=text, options=options) - return response.text - - -async def _rolling_summarize( - provider: OllamaLLMProvider, - *, - text: str, - options: dict[str, Any], -) -> tuple[str, list[SummarizationStep], int]: - """ - Summarize potentially long text by chunking and rolling summaries forward. - - Args: - provider: Initialized OllamaLLMProvider instance. - text: Full document text to summarize. - options: Provider options forwarded to the model. - - Returns: - tuple[str, list[SummarizationStep], int]: A tuple of (final_summary, steps_metadata, chunk_count). - """ - chunks = provider.chunk_text(text, max_tokens=provider.max_context_tokens) - if not chunks: - return "", [], 0 - - steps = [] - summary = await _summarize_once(provider, text=chunks[0].text, options=options) - steps.append( - SummarizationStep( - chunk_index=chunks[0].index, - input_tokens=chunks[0].token_count, - source="chunk", - ) - ) - - for chunk in chunks[1:]: - combined_text = f"{summary}\n\n{chunk.text}" - combined_tokens = provider.count_tokens(combined_text) - - if ( - provider.max_context_tokens - and combined_tokens > provider.max_context_tokens - ): - chunk_summary = await _summarize_once( - provider, - text=chunk.text, - options=options, - ) - steps.append( - SummarizationStep( - chunk_index=chunk.index, - input_tokens=chunk.token_count, - source="chunk", - ) - ) - combined_text = f"{summary}\n\n{chunk_summary}" - combined_tokens = provider.count_tokens(combined_text) - - if ( - provider.max_context_tokens - and combined_tokens > provider.max_context_tokens - ): - trimmed_parts = [] - for part in provider.chunk_text( - combined_text, max_tokens=provider.max_context_tokens - ): - tentative = " ".join(trimmed_parts + [part.text]) - if ( - provider.max_context_tokens - and provider.count_tokens(tentative) - > provider.max_context_tokens - ): - break - trimmed_parts.append(part.text) - combined_text = " ".join(trimmed_parts) - combined_tokens = provider.count_tokens(combined_text) - - summary = await _summarize_once( - provider, - text=combined_text, - options=options, - ) - steps.append( - SummarizationStep( - chunk_index=chunk.index, - input_tokens=combined_tokens, - source="summary+chunk", - ) - ) - - return summary, steps, len(chunks) - - -def _build_sse_message(payload: dict[str, Any]) -> str: - """ - Format a payload as an SSE data message. - - Args: - payload: Dictionary to serialize into the SSE data field. - - Returns: - str: Serialized SSE message string. - """ - return f"data: {json.dumps(payload)}\n\n" - - -def _trim_to_context(provider: OllamaLLMProvider, text: str) -> str: - """ - Trim combined text to respect the provider's context window. - - Args: - provider: Provider with a configured max_context_tokens. - text: Concatenated summary and chunk text. - - Returns: - str: Text trimmed to fit within the context limit. - """ - if not provider.max_context_tokens: - return text - - trimmed_parts: list[str] = [] - for part in provider.chunk_text(text, max_tokens=provider.max_context_tokens): - tentative = " ".join(trimmed_parts + [part.text]) - if ( - provider.max_context_tokens - and provider.count_tokens(tentative) > provider.max_context_tokens - ): - break - trimmed_parts.append(part.text) - - return " ".join(trimmed_parts) - - -@router.post("/summarize", response_model=SummarizationResponse) -async def summarize_document(payload: SummarizationRequest) -> SummarizationResponse: - """ - Summarize a document using a local Ollama model. - - The endpoint chunks inputs above the context limit and performs a rolling - summarization that keeps the interim summary in context for subsequent chunks. - """ - if not payload.text.strip(): - raise HTTPException(status_code=400, detail="Input text cannot be empty.") - - system_prompt = payload.system_prompt or _load_template_prompt( - information_to_extract=payload.information_to_extract, - entities_to_identify=payload.entities_to_identify, - ) - model_name = payload.model or DEFAULT_MODEL - options = {**DEFAULT_OPTIONS, **(payload.options or {})} - - try: - context_limit = int(options.get("num_ctx", DEFAULT_OPTIONS["num_ctx"])) - - except (TypeError, ValueError) as exc: - raise HTTPException( - status_code=400, - detail="num_ctx option must be an integer.", - ) from exc - - if context_limit <= 0: - raise HTTPException(status_code=400, detail="num_ctx must be greater than 0.") - - provider = _build_provider( - model_name, - tokenizer_name=payload.tokenizer, - system_prompt=system_prompt, - context_limit=context_limit, - ) - - summary, steps, chunk_count = await _rolling_summarize( - provider, - text=payload.text, - options=options, - ) - - return SummarizationResponse( - summary=summary, - model=provider.model_name, - system_prompt=system_prompt, - options=options, - chunks_used=chunk_count or 1, - steps=steps, - ) - - -@router.post("/summarize/stream") -async def stream_summarize_document( - payload: SummarizationRequest, -) -> StreamingResponse: - """ - Stream a document summary using server-sent events (text/event-stream). - - Emits: - - data: {"type": "meta", ...} - - data: {"type": "token", "chunk_index": int, "source": str, "text": str} - - data: {"type": "summary", "summary": str, "chunks_used": int, "steps": [...], "model": str} - """ - if not payload.text.strip(): - raise HTTPException(status_code=400, detail="Input text cannot be empty.") - - system_prompt = payload.system_prompt or _load_template_prompt( - information_to_extract=payload.information_to_extract, - entities_to_identify=payload.entities_to_identify, - ) - model_name = payload.model or DEFAULT_MODEL - options = {**DEFAULT_OPTIONS, **(payload.options or {})} - - try: - context_limit = int(options.get("num_ctx", DEFAULT_OPTIONS["num_ctx"])) - - except (TypeError, ValueError) as exc: - raise HTTPException( - status_code=400, - detail="num_ctx option must be an integer.", - ) from exc - - if context_limit <= 0: - raise HTTPException(status_code=400, detail="num_ctx must be greater than 0.") - - provider = _build_provider( - model_name, - tokenizer_name=payload.tokenizer, - system_prompt=system_prompt, - context_limit=context_limit, - ) - - async def event_stream() -> AsyncIterator[str]: - yield _build_sse_message( - { - "type": "meta", - "model": provider.model_name, - "system_prompt": system_prompt, - "options": options, - } - ) - - chunks = provider.chunk_text( - payload.text, - max_tokens=provider.max_context_tokens, - ) - - if not chunks: - yield _build_sse_message( - { - "type": "summary", - "summary": "", - "chunks_used": 0, - "steps": [], - "model": provider.model_name, - } - ) - return - - steps: list[dict[str, Any]] = [] - summary = "" - - for chunk in chunks: - source = "chunk" if not summary else "summary+chunk" - combined_text = f"{summary}\n\n{chunk.text}" if summary else chunk.text - combined_tokens = provider.count_tokens(combined_text) - - if ( - provider.max_context_tokens - and combined_tokens > provider.max_context_tokens - and summary - ): - # Summarize the current chunk alone when the combined context would overflow. - chunk_summary = "" - async for resp in provider.async_stream( - prompt=chunk.text, - options=options, - ): - token = resp.text - if not token: - continue - chunk_summary += token - yield _build_sse_message( - { - "type": "token", - "chunk_index": chunk.index, - "source": "chunk", - "text": token, - } - ) - - steps.append( - { - "chunk_index": chunk.index, - "input_tokens": chunk.token_count, - "source": "chunk", - } - ) - - combined_text = f"{summary}\n\n{chunk_summary}" - combined_text = _trim_to_context(provider, combined_text) - source = "summary+chunk" - combined_tokens = provider.count_tokens(combined_text) - - updated_summary = "" - async for resp in provider.async_stream( - prompt=combined_text, - options=options, - ): - token = resp.text - if not token: - continue - updated_summary += token - yield _build_sse_message( - { - "type": "token", - "chunk_index": chunk.index, - "source": source, - "text": token, - } - ) - - steps.append( - { - "chunk_index": chunk.index, - "input_tokens": combined_tokens, - "source": source, - } - ) - summary = updated_summary - - yield _build_sse_message( - { - "type": "summary", - "summary": summary, - "chunks_used": len(chunks), - "steps": steps, - "model": provider.model_name, - } - ) - - return StreamingResponse(event_stream(), media_type="text/event-stream") From 3c55d8e9240f70330036a5be33708688841ce7a8 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 07:57:56 -0300 Subject: [PATCH 043/101] =?UTF-8?q?=F0=9F=A6=96=20Changed=20aymurai/api/en?= =?UTF-8?q?dpoints/routers/misc/document=5Fextract.py=20for=20release/v1.5?= =?UTF-8?q?.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🦖 Changed aymurai/text/extractors/pdf.py for release/v1.5.0 compatibility 🦖 Changed aymurai/text/extractors/utils.py for release/v1.5.0 compatibility --- .../routers/misc/document_extract.py | 27 +-- aymurai/text/extractors/pdf.py | 26 +-- aymurai/text/extractors/utils.py | 218 +++++++----------- 3 files changed, 84 insertions(+), 187 deletions(-) diff --git a/aymurai/api/endpoints/routers/misc/document_extract.py b/aymurai/api/endpoints/routers/misc/document_extract.py index 7322b07a..11354a0e 100644 --- a/aymurai/api/endpoints/routers/misc/document_extract.py +++ b/aymurai/api/endpoints/routers/misc/document_extract.py @@ -77,14 +77,7 @@ def run_safe_text_extraction( def plain_text_extractor( file: UploadFile, use_cache: bool = True, - layout_batch_size: int = 8, - detection_batch_size: int = 8, - table_rec_batch_size: int = 8, - recognition_batch_size: int = 8, - ocr_error_batch_size: int = 8, - force_ocr: bool = False, - strip_existing_ocr: bool = True, - torch_device: str | None = None, + y_tolerance: float | None = None, debug: bool | None = None, ) -> Document: """ @@ -93,14 +86,7 @@ def plain_text_extractor( Args: file (UploadFile): Incoming document upload. use_cache (bool): Whether to use caching for the extraction. Defaults to True. - layout_batch_size (int): Batch size for layout model inference. Defaults to 8. - detection_batch_size (int): Batch size for detection model inference. Defaults to 8. - table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. - recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. - ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. - force_ocr (bool): Force OCR even if text is detected. Defaults to False. - strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. - torch_device (str | None): Optional override for the torch device. Defaults to None. + y_tolerance (float | None): Optional vertical tolerance for PDF paragraph merging. Defaults to None. debug (bool | None): Optional override for marker debug mode. Defaults to None. Returns: @@ -126,14 +112,7 @@ def plain_text_extractor( document = run_safe_text_extraction( tmp_filename, use_cache=use_cache, - layout_batch_size=layout_batch_size, - detection_batch_size=detection_batch_size, - table_rec_batch_size=table_rec_batch_size, - recognition_batch_size=recognition_batch_size, - ocr_error_batch_size=ocr_error_batch_size, - force_ocr=force_ocr, - strip_existing_ocr=strip_existing_ocr, - torch_device=torch_device, + y_tolerance=y_tolerance, debug=debug, ) diff --git a/aymurai/text/extractors/pdf.py b/aymurai/text/extractors/pdf.py index c4b273c2..19cec025 100644 --- a/aymurai/text/extractors/pdf.py +++ b/aymurai/text/extractors/pdf.py @@ -16,16 +16,9 @@ class PdfExtractor(BaseExtractor): def extract( self, path: Path, + y_tolerance: float | None = None, *, use_cache: bool = True, - layout_batch_size: int = 8, - detection_batch_size: int = 8, - table_rec_batch_size: int = 8, - recognition_batch_size: int = 8, - ocr_error_batch_size: int = 8, - force_ocr: bool = False, - strip_existing_ocr: bool = True, - torch_device: str | None = None, debug: bool | None = None, ) -> str: """ @@ -34,14 +27,6 @@ def extract( Args: path (Path): Input document path. use_cache (bool): Toggle extractor-level caching. Defaults to True. - layout_batch_size (int): Batch size for layout model inference. Defaults to 8. - detection_batch_size (int): Batch size for detection model inference. Defaults to 8. - table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. - recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. - ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. - force_ocr (bool): Force OCR even if text is detected. Defaults to False. - strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. - torch_device (str | None): Optional override for the torch device. Defaults to None. debug (bool | None): Optional override for marker debug mode. Defaults to None. Returns: @@ -60,14 +45,7 @@ def extract( try: text = pdf_to_text( file_path, - layout_batch_size=layout_batch_size, - detection_batch_size=detection_batch_size, - table_rec_batch_size=table_rec_batch_size, - recognition_batch_size=recognition_batch_size, - ocr_error_batch_size=ocr_error_batch_size, - force_ocr=force_ocr, - strip_existing_ocr=strip_existing_ocr, - torch_device=torch_device, + y_tolerance=y_tolerance, debug=debug, ) except (OSError, ValueError) as exc: diff --git a/aymurai/text/extractors/utils.py b/aymurai/text/extractors/utils.py index 6a98ba30..9c449fe0 100644 --- a/aymurai/text/extractors/utils.py +++ b/aymurai/text/extractors/utils.py @@ -1,20 +1,17 @@ -import os import unicodedata import xml.etree.ElementTree as ET import zipfile -from functools import cache from pathlib import Path from typing import Any -import markdown2 import xmltodict -from bs4 import BeautifulSoup from lxml import etree -from marker.converters.pdf import PdfConverter -from marker.models import create_model_dict -from marker.renderers.markdown import MarkdownRenderer from more_itertools import flatten +import pymupdf +import statistics +import numpy as np + from aymurai.logger import get_logger from aymurai.utils.misc import get_element, get_recursively @@ -23,12 +20,11 @@ BLOCK_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "blockquote", "pre"} -MarkerPdfConfig = dict[str, int | str | bool] ODT_NS = {"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0"} -def normalize_text(text: str) -> str: +def _normalize_text(text: str) -> str: """ Normalize Unicode output consistently across extractors. @@ -41,127 +37,87 @@ def normalize_text(text: str) -> str: return unicodedata.normalize("NFKC", text) -def markdown_to_text(md: str) -> str: +def _compute_median_margin_between_blocks(pdf_path: str) -> float: """ - Convert Markdown content to plain text by extracting relevant blocks. - + Computes the median vertical margin between text blocks in a PDF. Args: - md (str): Markdown content produced by the renderer. - + pdf_path (str): Path to the PDF file. Returns: - str: Plain text representation stripped of nested blocks. + float: Median margin between text blocks (in points). """ - html = markdown2.markdown(md, extras=["tables"]) - soup = BeautifulSoup(html, "html.parser") - - chunks: list[str] = [] - for block in soup.find_all(BLOCK_TAGS): - if block.find_parent(BLOCK_TAGS): - continue - chunks.append(block.get_text(" ", strip=True)) - - return "\n\n".join(filter(None, chunks)) - - -def _build_marker_pdf_config( - layout_batch_size: int = 8, - detection_batch_size: int = 8, - table_rec_batch_size: int = 8, - recognition_batch_size: int = 8, - ocr_error_batch_size: int = 8, - force_ocr: bool = False, - strip_existing_ocr: bool = True, - torch_device: str | None = None, - debug: bool | None = None, -) -> MarkerPdfConfig: - """ - Build marker configuration factoring in environment overrides. + margins = [] - Args: - layout_batch_size (int): Batch size for layout model inference. Defaults to 8. - detection_batch_size (int): Batch size for detection model inference. Defaults to 8. - table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. - recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. - ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. - force_ocr (bool): Force OCR even if text is detected. Defaults to False. - strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. - torch_device (str | None): Optional override for the torch device. Defaults to None. - debug (bool | None): Optional override for marker debug mode. Defaults to None. + with pymupdf.open(pdf_path) as doc: + for page in doc: + # Extract all text blocks from the page + blocks = page.get_text("blocks") - Returns: - dict[str, int | str | bool]: Effective configuration for marker-pdf. - """ - config: MarkerPdfConfig = { - "layout_batch_size": layout_batch_size, - "detection_batch_size": detection_batch_size, - "table_rec_batch_size": table_rec_batch_size, - "recognition_batch_size": recognition_batch_size, - "ocr_error_batch_size": ocr_error_batch_size, - "force_ocr": force_ocr, - "strip_existing_ocr": strip_existing_ocr, - } - - if torch_device is None: - torch_device = os.getenv("TORCH_DEVICE") - if torch_device: - config["TORCH_DEVICE"] = torch_device - - if debug is None: - log_level = os.getenv("LOG_LEVEL", "").lower() - debug = log_level == "debug" - - if debug: - config["debug"] = True - - return config - - -@cache -def get_marker_pdf_converter_and_md_renderer( - config_items: tuple[tuple[str, int | str | bool], ...], -) -> tuple[PdfConverter, MarkdownRenderer]: - """ - Provide cached marker PDF converter and Markdown renderer instances. + # Sort blocks by their top y-coordinate (y0) + blocks_sorted = sorted(blocks, key=lambda b: b[1]) - Args: - config_items (tuple[tuple[str, int | str | bool], ...]): Sorted config items - to build a stable cache key. + # Compute vertical margins between consecutive blocks + for i in range(1, len(blocks_sorted)): + previous_block = blocks_sorted[i - 1] + current_block = blocks_sorted[i] + + # Calculate the vertical margin + previous_y1 = previous_block[3] # Bottom of the previous block + current_y0 = current_block[1] # Top of the current block + margin = current_y0 - previous_y1 + + if margin > 0: # Ignore overlapping blocks + margins.append(margin) + + # Compute and return the median margin + if margins: + return statistics.median(margins) + else: + return 0.0 # Return 0 if no margins were found + +def _extract_and_merge_paragraphs(pdf_path: str, y_tolerance=5) -> list[str]: + """ + Extracts and merges paragraphs from a PDF by grouping close text blocks. + Args: + pdf_path (str): Path to the PDF file. + y_tolerance (float): Maximum vertical gap (in points) to consider blocks part of the same paragraph. Returns: - tuple[PdfConverter, MarkdownRenderer]: Ready-to-use converter and renderer. + list[str]: A list of merged paragraphs as strings. """ - pdf_converter = PdfConverter( - artifact_dict=create_model_dict(), - config=dict(config_items), - ) + paragraphs = [] + current_paragraph = [] + last_y1 = None - markdown_renderer = MarkdownRenderer( - { - "keep_pageheader_in_output": True, - "keep_pagefooter_in_output": True, - } - ) + with pymupdf.open(pdf_path) as doc: + for page in doc: + # Extract all text blocks from the page + blocks = page.get_text("blocks") + + # Sort blocks by their top y-coordinate (y0) + blocks_sorted = sorted(blocks, key=lambda b: b[1]) + + for block in blocks_sorted: + x0, y0, x1, y1, text, *_ = block - return pdf_converter, markdown_renderer + if last_y1 is not None and (y0 - last_y1) > y_tolerance: + # If the gap between blocks is too large, start a new paragraph + if current_paragraph: + paragraphs.append(" ".join(current_paragraph)) + current_paragraph = [] + current_paragraph.append(text) + last_y1 = y1 -def _marker_config_key( - config: MarkerPdfConfig, -) -> tuple[tuple[str, int | str | bool], ...]: - return tuple(sorted(config.items())) + if current_paragraph: + paragraphs.append(" ".join(current_paragraph)) + current_paragraph = [] + + return paragraphs def pdf_to_text( - file_path: Path, - *, - layout_batch_size: int = 8, - detection_batch_size: int = 8, - table_rec_batch_size: int = 8, - recognition_batch_size: int = 8, - ocr_error_batch_size: int = 8, - force_ocr: bool = False, - strip_existing_ocr: bool = True, - torch_device: str | None = None, + file_path: Path | str, + y_tolerance: float | None = None, debug: bool | None = None, ) -> str: """ @@ -169,38 +125,22 @@ def pdf_to_text( Args: file_path (Path): Path to the PDF document. - layout_batch_size (int): Batch size for layout model inference. Defaults to 8. - detection_batch_size (int): Batch size for detection model inference. Defaults to 8. - table_rec_batch_size (int): Batch size for table recognition. Defaults to 8. - recognition_batch_size (int): Batch size for OCR recognition. Defaults to 8. - ocr_error_batch_size (int): Batch size for OCR error correction. Defaults to 8. - force_ocr (bool): Force OCR even if text is detected. Defaults to False. - strip_existing_ocr (bool): Remove embedded OCR layers before re-OCR. Defaults to True. - torch_device (str | None): Optional override for the torch device. Defaults to None. + y_tolerance (float, optional): + Maximum vertical gap (in points) to consider blocks part of the same paragraph. debug (bool | None): Optional override for marker debug mode. Defaults to None. Returns: str: Cleaned textual content extracted from the PDF. """ logger.info("Extracting text from PDF: %s", file_path) - config = _build_marker_pdf_config( - layout_batch_size=layout_batch_size, - detection_batch_size=detection_batch_size, - table_rec_batch_size=table_rec_batch_size, - recognition_batch_size=recognition_batch_size, - ocr_error_batch_size=ocr_error_batch_size, - force_ocr=force_ocr, - strip_existing_ocr=strip_existing_ocr, - torch_device=torch_device, - debug=debug, - ) - pdf_converter, markdown_renderer = get_marker_pdf_converter_and_md_renderer( - _marker_config_key(config) - ) - document = pdf_converter.build_document(filepath=file_path.as_posix()) - markdown_output = markdown_renderer(document) - plain_text = markdown_to_text(markdown_output.markdown) - return normalize_text(plain_text) + + if y_tolerance is None: + y_tolerance = _compute_median_margin_between_blocks(file_path) + + paragraphs = _extract_and_merge_paragraphs(file_path, np.ceil(y_tolerance)) + docu = "\n\n".join(paragraphs) + + return _normalize_text(docu) def load_xml_from_docx(path: Path, xmlfile: str = "word/footnotes.xml") -> Any | None: From 60ba41422bd0fc923b7ee9975a31c7f92e86bf26 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 08:39:36 -0300 Subject: [PATCH 044/101] =?UTF-8?q?=E2=8F=AA=20Revert=20aymurai/api/main.p?= =?UTF-8?q?y=20to=20a801bf4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/api/main.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/aymurai/api/main.py b/aymurai/api/main.py index fbd5bdad..d814a3f6 100644 --- a/aymurai/api/main.py +++ b/aymurai/api/main.py @@ -6,15 +6,14 @@ from alembic import command from alembic.config import Config from fastapi import FastAPI, Request -from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse +from fastapi.middleware.cors import CORSMiddleware from aymurai.api import core -from aymurai.api.startup.database import check_db_connection -from aymurai.api.startup.marker import warm_marker_models from aymurai.logger import get_logger -from aymurai.pipeline import AymurAIPipeline from aymurai.settings import settings +from aymurai.pipeline import AymurAIPipeline +from aymurai.api.startup.database import check_db_connection try: from aymurai.version import __version__ @@ -105,11 +104,10 @@ def healthcheck(): if __name__ == "__main__": # download the necessary data - logger.info("Loading pipelines") + logger.info("Loading pipelines and exit.") AymurAIPipeline.load( os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "flair-anonymizer") ) AymurAIPipeline.load( os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "full-paragraph") ) - warm_marker_models() From 596d3b19e06ce8cae93c74cb25d4e95fe1dc55b0 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 08:42:12 -0300 Subject: [PATCH 045/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20aymurai/api/st?= =?UTF-8?q?artup/marker.py=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/api/startup/marker.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 aymurai/api/startup/marker.py diff --git a/aymurai/api/startup/marker.py b/aymurai/api/startup/marker.py deleted file mode 100644 index fa40aa48..00000000 --- a/aymurai/api/startup/marker.py +++ /dev/null @@ -1,18 +0,0 @@ -from aymurai.logger import get_logger -from aymurai.text.extractors.utils import ( - _build_marker_pdf_config, - _marker_config_key, - get_marker_pdf_converter_and_md_renderer, -) - -logger = get_logger(__name__) - - -def warm_marker_models() -> None: - """Download marker-pdf artifacts at startup to avoid first-request latency.""" - try: - config = _build_marker_pdf_config() - get_marker_pdf_converter_and_md_renderer(_marker_config_key(config)) - logger.info("marker-pdf models are ready") - except Exception as exc: - logger.warning("marker-pdf warmup failed: %s", exc) From 994aca42aae80e3663dda8534730328abf186f1a Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 08:51:58 -0300 Subject: [PATCH 046/101] =?UTF-8?q?=F0=9F=94=A5=20=20aymurai/experiments/e?= =?UTF-8?q?ntity=5Fdisambiguation=20folder=20for=20release/v1.5.0=20compat?= =?UTF-8?q?ibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity_disambiguation/__init__.py | 1 - .../entity_disambiguation/config.py | 176 ------- .../entity_disambiguation/manifest.py | 51 -- .../entity_disambiguation/mlflow_logging.py | 111 ----- .../entity_disambiguation/runner.py | 468 ------------------ 5 files changed, 807 deletions(-) delete mode 100644 aymurai/experiments/entity_disambiguation/__init__.py delete mode 100644 aymurai/experiments/entity_disambiguation/config.py delete mode 100644 aymurai/experiments/entity_disambiguation/manifest.py delete mode 100644 aymurai/experiments/entity_disambiguation/mlflow_logging.py delete mode 100644 aymurai/experiments/entity_disambiguation/runner.py diff --git a/aymurai/experiments/entity_disambiguation/__init__.py b/aymurai/experiments/entity_disambiguation/__init__.py deleted file mode 100644 index 17dc9d09..00000000 --- a/aymurai/experiments/entity_disambiguation/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Entity disambiguation experiment helpers.""" diff --git a/aymurai/experiments/entity_disambiguation/config.py b/aymurai/experiments/entity_disambiguation/config.py deleted file mode 100644 index 68f54534..00000000 --- a/aymurai/experiments/entity_disambiguation/config.py +++ /dev/null @@ -1,176 +0,0 @@ -import os -from datetime import datetime, timezone -from typing import Literal - -from pydantic import BaseModel, ConfigDict, Field - -from aymurai.utils.yaml_data import load_yaml - - -class ExperimentConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - name: str - run_name: str - - -class ModelConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - provider: str - name: str - temperature: float | None = None - max_tokens: int | None = None - - -class PromptSpec(BaseModel): - model_config = ConfigDict(extra="forbid") - - id: str - text: str | None = None - - -class PromptsConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - system: PromptSpec - user: PromptSpec - - -class DataManifestConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - enabled: bool = True - mode: Literal["metadata_only"] = "metadata_only" - - -class DataConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - ground_truth_dir: str - input_dir: str - dataset_id: str | None = None - manifest: DataManifestConfig = DataManifestConfig() - - -class PredictionsConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - output_base_dir: str - dir_name_template: str - - -class MLflowConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - tracking_uri: str - experiment_name: str - - -class LoggingPrivacyConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - log_prompt_text: bool = True - log_data_manifest: bool = False - log_per_doc_scores: bool = True - - -class PerDocScoresConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - id_strategy: str = "anon_filename" - - -class LoggingConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - mlflow: MLflowConfig - privacy: LoggingPrivacyConfig = LoggingPrivacyConfig() - per_doc_scores: PerDocScoresConfig = PerDocScoresConfig() - - -class EvaluationConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - function: str = "evaluate_disambiguation" - weights: dict[str, float] = Field( - default_factory=lambda: { - "w_ent": 0.4, - "w_alias": 0.35, - "w_label": 0.2, - "w_role": 0.05, - } - ) - per_doc_scores: bool = True - sim_threshold: float | None = None - normalize: bool = True - - -class ExperimentRunConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - experiment: ExperimentConfig - model: ModelConfig - prompts: PromptsConfig - data: DataConfig - predictions: PredictionsConfig - logging: LoggingConfig - evaluation: EvaluationConfig - - -def load_experiment_config(path: str) -> ExperimentRunConfig: - """ - Load an experiment configuration from a YAML file. - - Args: - path (str): Path to the YAML configuration file. - - Raises: - ValueError: If the MLflow tracking URI is not set in the environment or configuration. - - Returns: - ExperimentRunConfig: The loaded experiment configuration. - """ - data = load_yaml(path) - tracking_uri = os.getenv("MLFLOW_TRACKING_URI") - if tracking_uri: - logging_cfg = data.setdefault("logging", {}) - mlflow_cfg = logging_cfg.setdefault("mlflow", {}) - mlflow_cfg["tracking_uri"] = tracking_uri - else: - mlflow_cfg = data.get("logging", {}).get("mlflow", {}) - if "tracking_uri" not in mlflow_cfg: - raise ValueError( - "MLFLOW_TRACKING_URI is required but was not set in the environment." - ) - return ExperimentRunConfig.model_validate(data) - - -def render_run_name( - template: str, - model_name: str, - system_id: str, - user_id: str, - *, - timestamp: str | None = None, -) -> str: - """ - Render a run name based on a template and provided identifiers. - - Args: - template (str): The template string for the run name. - model_name (str): The name of the model. - system_id (str): The system identifier. - user_id (str): The user identifier. - timestamp (str | None, optional): The timestamp string. Defaults to None. - - Returns: - str: The rendered run name. - """ - timestamp = timestamp or datetime.now(timezone.utc).strftime("%y%m%d_%H%M") - return template.format( - model=model_name, - system_id=system_id, - user_id=user_id, - timestamp=timestamp, - ) diff --git a/aymurai/experiments/entity_disambiguation/manifest.py b/aymurai/experiments/entity_disambiguation/manifest.py deleted file mode 100644 index 2e2c37fa..00000000 --- a/aymurai/experiments/entity_disambiguation/manifest.py +++ /dev/null @@ -1,51 +0,0 @@ -import hashlib -import json -from dataclasses import dataclass -from pathlib import Path - - -@dataclass(frozen=True) -class DatasetInfo: - dataset_hash: str - dataset_count: int - dataset_bytes_total: int - manifest: dict | None = None - - -def build_metadata_manifest(root_dir: Path) -> DatasetInfo: - """ - Build a metadata manifest for the dataset located at the given root directory. - - Args: - root_dir (Path): The root directory of the dataset. - - Returns: - DatasetInfo: An object containing dataset hash, count, total bytes, and manifest. - """ - files = [] - total_bytes = 0 - - for path in sorted(root_dir.rglob("*")): - if not path.is_file(): - continue - stat = path.stat() - rel_path = path.relative_to(root_dir).as_posix() - files.append( - { - "path": rel_path, - "size_bytes": stat.st_size, - "mtime": int(stat.st_mtime), - } - ) - total_bytes += stat.st_size - - manifest = {"files": files, "summary": {"count": len(files), "bytes": total_bytes}} - manifest_bytes = json.dumps(files, sort_keys=True).encode("utf-8") - dataset_hash = hashlib.sha256(manifest_bytes).hexdigest() - - return DatasetInfo( - dataset_hash=dataset_hash, - dataset_count=len(files), - dataset_bytes_total=total_bytes, - manifest=manifest, - ) diff --git a/aymurai/experiments/entity_disambiguation/mlflow_logging.py b/aymurai/experiments/entity_disambiguation/mlflow_logging.py deleted file mode 100644 index b9e0a799..00000000 --- a/aymurai/experiments/entity_disambiguation/mlflow_logging.py +++ /dev/null @@ -1,111 +0,0 @@ -import json -import tempfile -from pathlib import Path - -import mlflow - -from aymurai.experiments.entity_disambiguation.config import ExperimentRunConfig -from aymurai.experiments.entity_disambiguation.manifest import DatasetInfo -from aymurai.utils.yaml_data import save_yaml - - -def configure_mlflow(config: ExperimentRunConfig) -> None: - """ - Configure MLflow tracking URI and experiment name. - - Args: - config (ExperimentRunConfig): Experiment run configuration. - """ - mlflow.set_tracking_uri(config.logging.mlflow.tracking_uri) - mlflow.set_experiment(config.logging.mlflow.experiment_name) - - -def log_run_metadata( - config: ExperimentRunConfig, - *, - run_name: str | None = None, - preds_dir_name: str | None = None, - dataset_info: DatasetInfo | None = None, - metrics: dict[str, float] | None = None, - per_doc_scores: dict[str, object] | None = None, -) -> None: - """ - Log run metadata to MLflow. - - Args: - config (ExperimentRunConfig): Experiment run configuration. - run_name (str | None, optional): Run name. Defaults to None. - preds_dir_name (str | None, optional): Predictions directory name. Defaults to None. - dataset_info (DatasetInfo | None, optional): Dataset information. Defaults to None. - metrics (dict[str, float] | None, optional): Metrics to log. Defaults to None. - per_doc_scores (dict[str, object] | None, optional): Per-document scores to log. Defaults to None. - """ - params = { - "model_provider": config.model.provider, - "model_name": config.model.name, - "system_prompt_id": config.prompts.system.id, - "user_prompt_id": config.prompts.user.id, - "preds_dir_template": config.predictions.dir_name_template, - } - if run_name: - params["run_name"] = run_name - if preds_dir_name: - params["preds_dir_name"] = preds_dir_name - if config.model.temperature is not None: - params["temperature"] = config.model.temperature - if config.model.max_tokens is not None: - params["max_tokens"] = config.model.max_tokens - if config.data.dataset_id: - params["dataset_id"] = config.data.dataset_id - - if dataset_info: - params.update( - { - "dataset_hash": dataset_info.dataset_hash, - "dataset_count": dataset_info.dataset_count, - "dataset_bytes_total": dataset_info.dataset_bytes_total, - } - ) - - mlflow.log_params(params) - - if metrics: - mlflow.log_metrics(metrics) - - with tempfile.TemporaryDirectory() as tmp_dir: - tmp_path = Path(tmp_dir) - config_payload = config.model_dump(mode="json") - if not config.logging.privacy.log_prompt_text: - config_payload["prompts"]["system"]["text"] = "" - config_payload["prompts"]["user"]["text"] = "" - config_path = tmp_path / "config.yaml" - save_yaml(config_payload, str(config_path)) - mlflow.log_artifact(str(config_path), artifact_path="config") - - if config.logging.privacy.log_prompt_text: - system_path = tmp_path / "system_prompt.txt" - user_path = tmp_path / "user_prompt.txt" - system_path.write_text(config.prompts.system.text or "", encoding="utf-8") - user_path.write_text(config.prompts.user.text or "", encoding="utf-8") - mlflow.log_artifact(str(system_path), artifact_path="prompts") - mlflow.log_artifact(str(user_path), artifact_path="prompts") - - if ( - config.logging.privacy.log_data_manifest - and dataset_info - and dataset_info.manifest - ): - manifest_path = tmp_path / "manifest.json" - manifest_path.write_text( - json.dumps(dataset_info.manifest, indent=2, sort_keys=True), - encoding="utf-8", - ) - mlflow.log_artifact(str(manifest_path), artifact_path="data") - - if config.logging.privacy.log_per_doc_scores and per_doc_scores: - scores_path = tmp_path / "per_doc_scores.json" - scores_path.write_text( - json.dumps(per_doc_scores, indent=2, sort_keys=True), - encoding="utf-8", - ) - mlflow.log_artifact(str(scores_path), artifact_path="metrics") diff --git a/aymurai/experiments/entity_disambiguation/runner.py b/aymurai/experiments/entity_disambiguation/runner.py deleted file mode 100644 index 2cc372ce..00000000 --- a/aymurai/experiments/entity_disambiguation/runner.py +++ /dev/null @@ -1,468 +0,0 @@ -import argparse -import json -import mimetypes -import os -import re -import time -from pathlib import Path -from typing import Any, Iterable - -import mlflow -import requests -from more_itertools import unique_everseen -from tqdm import tqdm - -from aymurai.evaluation.metrics import evaluate_disambiguation -from aymurai.experiments.entity_disambiguation.config import ( - load_experiment_config, - render_run_name, -) -from aymurai.experiments.entity_disambiguation.manifest import build_metadata_manifest -from aymurai.experiments.entity_disambiguation.mlflow_logging import ( - configure_mlflow, - log_run_metadata, -) -from aymurai.llm_providers import OllamaLLMProvider -from aymurai.logger import get_logger -from aymurai.meta.entities import CanonicalEntities, CanonicalEntity -from aymurai.utils.json_data import get_pretty, load_json, save_json -from aymurai.utils.paths import prediction_filename_for_test, test_id_from_filename - -logger = get_logger(__name__) - -DOC_EXTENSIONS = {".pdf", ".docx"} - - -def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]: - """ - Discover documents in a directory with given extensions. - - Args: - root (Path): Root directory to search for documents. - extensions (Iterable[str]): File extensions to include. - - Returns: - list[Path]: List of discovered document paths with the specified extensions. - """ - extensions = {ext.lower() for ext in extensions} - return sorted( - path - for path in root.rglob("*") - if path.is_file() and path.suffix.lower() in extensions - ) - - -def call_extraction_api( - session: requests.Session, endpoint: str, file_path: Path, timeout_s: float -) -> dict[str, object]: - """ - Call the extraction API with a document file. - - Args: - session (requests.Session): HTTP session for making requests. - endpoint (str): URL of the extraction API endpoint. - file_path (Path): Path to the document file to be processed. - timeout_s (float): Request timeout in seconds. - - Returns: - dict[str, object]: Payload containing the response details. - """ - payload: dict[str, object] = { - "path": str(file_path), - "status": "failure", - "status_code": None, - "elapsed_s": None, - "detail": None, - } - - if not file_path.exists(): - payload["detail"] = "File does not exist" - return payload - - mime_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream" - files = { - "file": (file_path.name, file_path.open("rb"), mime_type), - } - - try: - start = time.perf_counter() - response = session.post( - endpoint, - files=files, - timeout=timeout_s, - ) - elapsed = time.perf_counter() - start - except requests.RequestException as exc: - payload["detail"] = f"Request failed: {exc}" - return payload - finally: - files["file"][1].close() - - payload["status_code"] = response.status_code - payload["elapsed_s"] = elapsed - - try: - response_body = response.json() - except ValueError: - response_body = {"raw": response.text[:500]} - - if response.ok: - payload["status"] = "success" - payload["detail"] = { - "document_id": response_body.get("document_id"), - "document": response_body.get("document", []), - } - else: - payload["detail"] = response_body - - return payload - - -def get_predictions( - session: requests.Session, endpoint: str, sample: str, timeout_s: float -) -> dict: - """ - Get predictions from the prediction API for a given text sample. - - Args: - session (requests.Session): HTTP session for making requests. - endpoint (str): URL of the prediction API endpoint. - sample (str): Text sample to be predicted. - timeout_s (float): Request timeout in seconds. - - Returns: - dict: Prediction response as a dictionary. - """ - response = session.post(url=endpoint, json={"text": sample}, timeout=timeout_s) - response.raise_for_status() - return response.json() - - -def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]: - """ - Parse prediction labels to extract unique aymurai labels and their alternative texts. - - Args: - predictions (list[dict[str, Any]]): List of prediction dictionaries. - - Returns: - list[dict[str, str]]: List of dictionaries containing unique aymurai labels and their alternative texts. - """ - attrs_stream = ( - label.get("attrs") or {} - for label in (label for pred in predictions for label in pred.get("labels", ())) - ) - - unique_pairs = unique_everseen( - ( - attrs.get("aymurai_label"), - attrs.get("aymurai_alt_text"), - ) - for attrs in attrs_stream - if attrs.get("aymurai_label") and attrs.get("aymurai_alt_text") - ) - - return sorted( - ({"aymurai_label": label, "text": text} for label, text in unique_pairs), - key=lambda item: (item["aymurai_label"], item["text"]), - ) - - -def sanitize_filename(path: Path) -> str: - """ - Sanitize a filename by replacing spaces and underscores with hyphens and collapsing multiple hyphens. - - Args: - path (Path): Path object representing the file. - - Returns: - str: Sanitized filename as a string. - """ - base = os.path.splitext(path.name)[0] - target = re.sub(r"\s+|_", "-", base) - target = re.sub(r"-{2,}", "-", target) - return target - - -def extract_canonical_entities( - *, - session: requests.Session, - doc_path: Path, - extract_endpoint: str, - predict_endpoint: str, - timeout_s: float, - model_name: str, - system_prompt: str, - user_prompt_template: str, - temperature: float | None, - max_tokens: int | None, -) -> list[CanonicalEntity]: - """ - Extract canonical entities from a document using extraction and prediction APIs. - - Args: - session (requests.Session): HTTP session for making requests. - doc_path (Path): Path to the document file. - extract_endpoint (str): URL of the extraction API endpoint. - predict_endpoint (str): URL of the prediction API endpoint. - timeout_s (float): Request timeout in seconds. - model_name (str): Name of the LLM model to use. - system_prompt (str): System prompt text. - user_prompt_template (str): User prompt template text. - temperature (float | None): Temperature setting for the LLM. - max_tokens (int | None): Maximum tokens setting for the LLM. - - Raises: - ValueError: If the document text is empty or not found. - - Returns: - list[CanonicalEntity]: List of extracted canonical entities. - """ - document_payload = call_extraction_api( - session, - extract_endpoint, - doc_path, - timeout_s, - ) - document = document_payload.get("detail", {}).get("document") - - if not document: - raise ValueError("Document text is empty or not found.") - - ner_predictions = [ - get_predictions(session, predict_endpoint, paragraph, timeout_s) - for paragraph in tqdm(document, desc=f"NER {doc_path.name}") - ] - parsed_ner_labels = parse_prediction_labels(ner_predictions) - ner_output_json = get_pretty(parsed_ner_labels) - - user_prompt = user_prompt_template.format( - document_text="\n".join(document).strip(), - ner_output_json=ner_output_json, - ) - - provider = OllamaLLMProvider(model=model_name) - options: dict[str, object] = {} - if temperature is not None: - options["temperature"] = temperature - if max_tokens is not None: - options["num_predict"] = max_tokens - - generate_kwargs: dict[str, object] = { - "messages": [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_prompt}, - ], - "format": CanonicalEntities.model_json_schema(), - } - if options: - generate_kwargs["options"] = options - - response = provider.generate(**generate_kwargs) - - payload = json.loads(response.text) - canonical_entities = [ - CanonicalEntity.model_validate(output) - for output in payload.get("canonical_entities", []) - ] - - return canonical_entities - - -def run_experiment(config_path: str) -> None: - """ - Run the entity disambiguation experiment based on the provided configuration. - - Args: - config_path (str): Path to the experiment configuration file. - - Raises: - FileNotFoundError: If the input directory is not found. - FileNotFoundError: If the ground truth directory is not found. - RuntimeError: If no documents are found in the input directory. - ValueError: If there is an error in processing the documents. - """ - config = load_experiment_config(config_path) - - input_dir = Path(config.data.input_dir) - ground_truth_dir = Path(config.data.ground_truth_dir) - - if not input_dir.exists(): - raise FileNotFoundError(f"Input directory not found: {input_dir}") - if not ground_truth_dir.exists(): - raise FileNotFoundError(f"Ground truth directory not found: {ground_truth_dir}") - - documents = discover_documents(input_dir, DOC_EXTENSIONS) - if not documents: - raise RuntimeError(f"No documents found under {input_dir}") - - timestamp = time.strftime("%y%m%d_%H%M") - run_name = render_run_name( - config.experiment.run_name, - config.model.name, - config.prompts.system.id, - config.prompts.user.id, - timestamp=timestamp, - ) - preds_dir_name = render_run_name( - config.predictions.dir_name_template, - config.model.name, - config.prompts.system.id, - config.prompts.user.id, - timestamp=timestamp, - ) - output_dir = Path(config.predictions.output_base_dir) / preds_dir_name - output_dir.mkdir(parents=True, exist_ok=False) - - dataset_info = ( - build_metadata_manifest(ground_truth_dir) - if config.data.manifest.enabled - else None - ) - - api_base_url = os.getenv("API_BASE_URL", "http://localhost:8899") - extract_endpoint = f"{api_base_url}/misc/document-extract" - predict_endpoint = f"{api_base_url}/anonymizer/predict" - timeout_s = float(os.getenv("REQUEST_TIMEOUT", "30")) - - logger.info(f"Processing {len(documents)} documents from {input_dir}") - logger.info(f"Predictions will be stored in {output_dir}") - - configure_mlflow(config) - - per_doc_scores: dict[str, object] = {} - metrics_summary: dict[str, float] = {} - - with mlflow.start_run(run_name=run_name): - session = requests.Session() - - processed = 0 - failed = 0 - - for doc_path in documents: - logger.info(f"Processing document: {doc_path.name}") - try: - canonical_entities = extract_canonical_entities( - session=session, - doc_path=doc_path, - extract_endpoint=extract_endpoint, - predict_endpoint=predict_endpoint, - timeout_s=timeout_s, - model_name=config.model.name, - system_prompt=config.prompts.system.text or "", - user_prompt_template=config.prompts.user.text or "", - temperature=config.model.temperature, - max_tokens=config.model.max_tokens, - ) - target_filename = sanitize_filename(doc_path) - target_path = output_dir / f"{target_filename}.json" - save_json( - [ - entity.model_dump() - | { - "entity_id": entity.entity_id.hex - if entity.entity_id - else None - } - for entity in canonical_entities - ], - str(target_path), - ) - processed += 1 - except Exception as exc: - failed += 1 - logger.warning(f"Error processing {doc_path.name}: {exc}") - - if config.evaluation.function != "evaluate_disambiguation": - raise ValueError( - f"Unsupported evaluation function: {config.evaluation.function}" - ) - - results = [] - if output_dir.exists(): - for test_path in sorted(ground_truth_dir.glob("*.json")): - doc_id = test_id_from_filename(test_path) - pred_path = output_dir / prediction_filename_for_test(test_path) - - if not pred_path.exists(): - logger.warning( - f"Prediction for {test_path.name} was not found; skipping." - ) - continue - - gold_data = load_json(str(test_path)) - pred_data = load_json(str(pred_path)) - - score, metrics = evaluate_disambiguation( - gold_data, - pred_data, - w_ent=config.evaluation.weights.get("w_ent", 0.4), - w_alias=config.evaluation.weights.get("w_alias", 0.35), - w_label=config.evaluation.weights.get("w_label", 0.2), - w_role=config.evaluation.weights.get("w_role", 0.05), - sim_threshold=config.evaluation.sim_threshold or 0.3, - normalize=config.evaluation.normalize, - ) - results.append((doc_id, score, metrics)) - - average = ( - sum(score for _, score, _ in results) / len(results) - if results - else float("nan") - ) - - if results: - metrics_summary["score_avg"] = average - metrics_summary["doc_count"] = float(len(results)) - - metric_names = set(results[0][2].keys()) - for metric_name in metric_names: - metrics_summary[f"{metric_name}_avg"] = sum( - metrics.get(metric_name, 0.0) for _, _, metrics in results - ) / len(results) - - for doc_id, score, metrics in results: - per_doc_scores[doc_id] = {"score": score, **metrics} - - metrics_summary["docs_processed"] = float(processed) - metrics_summary["docs_failed"] = float(failed) - - log_run_metadata( - config, - run_name=run_name, - preds_dir_name=preds_dir_name, - dataset_info=dataset_info, - metrics=metrics_summary if metrics_summary else None, - per_doc_scores=per_doc_scores if per_doc_scores else None, - ) - - -def build_arg_parser() -> argparse.ArgumentParser: - """ - Build the argument parser for the entity disambiguation experiment. - - Returns: - argparse.ArgumentParser: The configured argument parser. - """ - parser = argparse.ArgumentParser( - description="Run entity disambiguation experiment from a YAML config.", - ) - parser.add_argument( - "--config", - required=True, - help="Path to the experiment YAML config.", - ) - return parser - - -def main() -> None: - """ - Main entry point for the entity disambiguation experiment script. - Parses command-line arguments and runs the experiment. - """ - args = build_arg_parser().parse_args() - run_experiment(args.config) - - -if __name__ == "__main__": - main() From 55de08685d1f497b7a32deed4b5867ed8ee3273c Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 08:54:33 -0300 Subject: [PATCH 047/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20aymurai/llm=5F?= =?UTF-8?q?providers=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/llm_providers/__init__.py | 9 - aymurai/llm_providers/ollama_provider.py | 254 ---------------------- aymurai/llm_providers/provider.py | 256 ----------------------- 3 files changed, 519 deletions(-) delete mode 100644 aymurai/llm_providers/__init__.py delete mode 100644 aymurai/llm_providers/ollama_provider.py delete mode 100644 aymurai/llm_providers/provider.py diff --git a/aymurai/llm_providers/__init__.py b/aymurai/llm_providers/__init__.py deleted file mode 100644 index c63aaaac..00000000 --- a/aymurai/llm_providers/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from aymurai.llm_providers.ollama_provider import OllamaLLMProvider -from aymurai.llm_providers.provider import DocumentChunk, LLMProvider, LLMResponse - -__all__ = [ - "DocumentChunk", - "LLMProvider", - "LLMResponse", - "OllamaLLMProvider", -] diff --git a/aymurai/llm_providers/ollama_provider.py b/aymurai/llm_providers/ollama_provider.py deleted file mode 100644 index b3e9ef80..00000000 --- a/aymurai/llm_providers/ollama_provider.py +++ /dev/null @@ -1,254 +0,0 @@ -from __future__ import annotations - -from typing import Any, AsyncIterator, Iterator - -import ollama -from ollama import AsyncClient - -from aymurai.llm_providers.provider import LLMProvider, LLMResponse - - -class OllamaLLMProvider(LLMProvider): - """Adapter that wraps `ollama.chat` preserving the common interface.""" - - def __init__( - self, - model: str, - *, - system_prompt: str | None = None, - keep_alive: int | str | None = None, - **kwargs, - ) -> None: - super().__init__(model=model, **kwargs) - self.system_prompt = system_prompt - self.keep_alive = keep_alive - - def generate( - self, - prompt: str | None = None, - *, - messages: list[dict[str, str]] | None = None, - **kwargs, - ) -> LLMResponse: - """ - Generate text using the configured Ollama model. - - Args: - prompt (str | None, optional): Optional single prompt string to be transformed into a chat message. Defaults to None. - messages (list[dict[str, str]] | None, optional): Optional list of pre-formatted chat messages to send. Defaults to None. - - Returns: - LLMResponse: Response containing the generated text, metadata about the request, and the raw payload. - """ - # Build the message payload - payload = self._build_messages(prompt=prompt, messages=messages) - - # Call ollama.chat - response = ollama.chat( - model=self.model_name, - messages=payload, - keep_alive=self.keep_alive, - **kwargs, - ) - - return self._build_llm_response( - response, - extra_metadata={ - "eval_count": response.get("eval_count"), - "eval_duration": response.get("eval_duration"), - }, - ) - - async def async_generate( - self, - prompt: str | None = None, - *, - messages: list[dict[str, str]] | None = None, - **kwargs, - ) -> LLMResponse: - """ - Asynchronously generate text using the configured Ollama model. - - Args: - prompt (str | None): Optional single prompt string to be transformed into a chat message. Defaults to None. - messages (list[dict[str, str]] | None): Optional list of pre-formatted chat messages to send. Defaults to None. - **kwargs: Additional keyword arguments forwarded to the Ollama AsyncClient chat endpoint. - - Returns: - LLMResponse: Response containing the generated text, metadata about the request, and the raw payload. - """ - # Build the message payload - payload = self._build_messages(prompt=prompt, messages=messages) - - # Call ollama.chat asynchronously - client = self._get_async_client() - response = await client.chat( - model=self.model_name, - messages=payload, - keep_alive=self.keep_alive, - **kwargs, - ) - - return self._build_llm_response( - response, - extra_metadata={ - "eval_count": response.get("eval_count"), - "eval_duration": response.get("eval_duration"), - }, - ) - - def stream( - self, - prompt: str | None = None, - *, - messages: list[dict[str, str]] | None = None, - **kwargs, - ) -> Iterator[LLMResponse]: - """ - Stream the response from the model using the provided prompt or messages. - - Args: - prompt (str | None, optional): Optional single prompt string to be transformed into a chat message. Defaults to None. - messages (list[dict[str, str]] | None, optional): Optional list of pre-formatted chat messages to send. Defaults to None. - - Yields: - Iterator[LLMResponse]: Response chunks containing generated text, metadata, and raw payloads. - """ - # Build the message payload - payload = self._build_messages(prompt=prompt, messages=messages) - - # Call ollama.chat with streaming enabled - stream_kwargs = {**kwargs, "stream": True} - - # Iterate over the streamed responses - for chunk in ollama.chat( - model=self.model_name, - messages=payload, - keep_alive=self.keep_alive, - **stream_kwargs, - ): - yield self._build_stream_response(chunk) - - async def async_stream( - self, - prompt: str | None = None, - *, - messages: list[dict[str, str]] | None = None, - **kwargs, - ) -> AsyncIterator[LLMResponse]: - """ - Asynchronously stream the response from the model using the provided prompt or messages. - - Args: - prompt (str | None, optional): Optional single prompt string to be transformed into a chat message. Defaults to None. - messages (list[dict[str, str]] | None, optional): Optional list of pre-formatted chat messages to send. Defaults to None. - - Yields: - AsyncIterator[LLMResponse]: Asynchronous iterator yielding response chunks containing generated text, metadata, and raw payloads. - """ - # Build the message payload - payload = self._build_messages(prompt=prompt, messages=messages) - - # Call ollama.chat with streaming enabled - stream_kwargs = {**kwargs, "stream": True} - - # Iterate over the streamed responses asynchronously - client = self._get_async_client() - stream = await client.chat( - model=self.model_name, - messages=payload, - keep_alive=self.keep_alive, - **stream_kwargs, - ) - async for chunk in stream: - yield self._build_stream_response(chunk) - - def _build_messages( - self, - *, - prompt: str | None, - messages: list[dict[str, str]] | None = None, - ) -> list[dict[str, str]]: - """ - Build the message payload for `ollama.chat`. - - Args: - prompt (str | None): The user prompt to be sent to the model. - messages (list[dict[str, str]] | None, optional): A list of message dictionaries to be sent to the model. - Defaults to None. - - Raises: - ValueError: If neither prompt nor messages are provided. - - Returns: - list[dict[str, str]]: The message payload to be sent to `ollama.chat`. - """ - if messages is not None: - return list(messages) - - if prompt is None: - raise ValueError("Either prompt or messages must be provided.") - - payload = [] - - if self.system_prompt: - payload.append({"role": "system", "content": self.system_prompt}) - - payload.append({"role": "user", "content": prompt}) - - return payload - - def _get_async_client(self) -> AsyncClient: - """ - Create a fresh AsyncClient instance. - - Returns: - AsyncClient: A new AsyncClient instance for making asynchronous requests. - """ - return AsyncClient() - - def _build_llm_response( - self, response: dict[str, Any], *, extra_metadata: dict[str, Any] | None = None - ) -> LLMResponse: - """ - Build an LLMResponse with consistent metadata from the response payload. - - Args: - response (dict[str, Any]): The response payload from the Ollama model. - extra_metadata (dict[str, Any] | None, optional): Additional metadata to include in the response. Defaults to None. - - Returns: - LLMResponse: The constructed LLMResponse object containing text, metadata, and raw response. - """ - # Extract the generated text - text = response.get("message", {}).get("content", "") - - # Build metadata - metadata = { - "model": self.model_name, - "provider": "ollama", - } - - # Include any extra metadata - if extra_metadata: - metadata.update(extra_metadata) - - return LLMResponse(text=text, metadata=metadata, raw=response) - - def _build_stream_response(self, chunk: dict[str, Any]) -> LLMResponse: - """ - Build stream responses with consistent metadata from chunk payloads. - - Args: - chunk (dict[str, Any]): The chunk payload from the Ollama model. - - Returns: - LLMResponse: The constructed LLMResponse object containing text, metadata, and raw chunk. - """ - return self._build_llm_response( - chunk, - extra_metadata={"done": chunk.get("done")}, - ) - - -__all__ = ["OllamaLLMProvider"] diff --git a/aymurai/llm_providers/provider.py b/aymurai/llm_providers/provider.py deleted file mode 100644 index 89e3750d..00000000 --- a/aymurai/llm_providers/provider.py +++ /dev/null @@ -1,256 +0,0 @@ -from __future__ import annotations - -import abc -from typing import Any, Sequence - -from pydantic import BaseModel, Field - - -class LLMResponse(BaseModel): - """Standard response wrapper for any LLM provider.""" - - text: str - metadata: dict[str, Any] = Field(default_factory=dict) - raw: Any | None = None - - -class DocumentChunk(BaseModel): - """Represents a chunked portion of a larger document.""" - - text: str - token_count: int - index: int - - -TokenizerType = Any - - -class LLMProvider(abc.ABC): - """Base class with shared utilities for concrete LLM providers.""" - - def __init__( - self, - model: str, - *, - tokenizer: TokenizerType | None = None, - max_context_tokens: int | None = None, - chunk_overlap: int = 0, - ) -> None: - self.model_name = model - self._tokenizer = tokenizer - self.max_context_tokens = max_context_tokens - self.chunk_overlap = max(chunk_overlap, 0) - - @abc.abstractmethod - def generate(self, prompt: str, **kwargs) -> LLMResponse: - """ - Generate a response from the model using the provided prompt. - - Args: - prompt (str): The input prompt to generate a response for. - **kwargs: Additional provider-specific parameters. - - Returns: - LLMResponse: The generated response wrapped in an LLMResponse object. - """ - pass - - @abc.abstractmethod - def stream(self, prompt: str, **kwargs): - """ - Stream the response from the model using the provided prompt. - - Args: - prompt (str): The input prompt to generate a response for. - **kwargs: Additional provider-specific parameters. - - Raises: - NotImplementedError: If the provider does not support streaming. - """ - raise NotImplementedError("This provider does not support streaming.") - - def count_tokens(self, text: str) -> int: - """ - Count the number of tokens in the given text. - - Args: - text (str): The input text to count tokens for. - - Returns: - int: The number of tokens in the input text. - """ - tokens = self._tokenize(text) - return len(tokens) - - def chunk_text( - self, - text: str, - *, - max_tokens: int | None = None, - overlap: int | None = None, - ) -> list[DocumentChunk]: - """ - Chunk text ensuring every piece respects the token budget. - - Args: - text (str): The input text to chunk. - max_tokens (int | None): Optional maximum tokens per chunk. - overlap (int | None): Optional token overlap between chunks. - - Returns: - list[DocumentChunk]: A list of document chunks. - """ - cleaned_text = text.strip() - if not cleaned_text: - return [] - - limit = max_tokens or self.max_context_tokens - if limit is None: - total_tokens = self.count_tokens(cleaned_text) - return [DocumentChunk(text=cleaned_text, token_count=total_tokens, index=0)] - - words = cleaned_text.split() - if not words: - return [] - - chunks = [] - overlap_tokens = overlap if overlap is not None else self.chunk_overlap - current_words = [] - current_tokens = 0 - - for word in words: - if not current_words: - current_words = [word] - current_tokens = self.count_tokens(" ".join(current_words)) - if current_tokens > limit: - self._append_chunk( - chunks=chunks, - text=current_words[0], - token_count=current_tokens, - ) - current_words = [] - current_tokens = 0 - continue - - tentative_words = current_words + [word] - tentative_text = " ".join(tentative_words) - tentative_tokens = self.count_tokens(tentative_text) - - if tentative_tokens <= limit: - current_words = tentative_words - current_tokens = tentative_tokens - continue - - chunk_text = " ".join(current_words) - self._append_chunk( - chunks=chunks, - text=chunk_text, - token_count=current_tokens, - ) - - carry_over = self._overlap_tail(current_words, overlap_tokens) - current_words = carry_over + [word] - current_tokens = self.count_tokens(" ".join(current_words)) - - if current_tokens > limit: - chunk_text = " ".join(current_words) - self._append_chunk( - chunks=chunks, - text=chunk_text, - token_count=current_tokens, - ) - current_words = [] - current_tokens = 0 - - if current_words: - chunk_text = " ".join(current_words) - self._append_chunk( - chunks=chunks, - text=chunk_text, - token_count=current_tokens, - fallback_count=True, - ) - - return chunks - - def _tokenize(self, text: str) -> Sequence[Any]: - """ - Tokenize text using the configured tokenizer or fallback to whitespace. - - Args: - text (str): The input text to tokenize. - - Returns: - Sequence[Any]: The sequence of tokens. - """ - if self._tokenizer is None: - return text.split() - - tokenizer = self._tokenizer - if hasattr(tokenizer, "encode"): - return tokenizer.encode(text) - - if callable(tokenizer): - tokens = tokenizer(text) - if isinstance(tokens, dict): - return tokens.get("input_ids", []) - return tokens - - return text.split() - - def _overlap_tail(self, words: list[str], overlap_tokens: int) -> list[str]: - """ - Get the tail overlap of words based on the token budget. - - Args: - words (list[str]): The list of words to consider for overlap. - overlap_tokens (int): The token budget for the overlap. Must be non-negative. - - Returns: - list[str]: The list of words representing the tail overlap. - """ - assert overlap_tokens >= 0, "`overlap_tokens` must be non-negative." - - if not words or overlap_tokens == 0: - return [] - - tail = [] - - for word in reversed(words): - tail.insert(0, word) - token_budget = self.count_tokens(" ".join(tail)) - if token_budget >= overlap_tokens: - break - - return tail - - def _append_chunk( - self, - *, - chunks: list[DocumentChunk], - text: str, - token_count: int, - fallback_count: bool = False, - ) -> None: - """ - Append a chunk with consistent indexing, optionally recomputing token count if missing. - - Args: - chunks (list[DocumentChunk]): Accumulated chunk list. - text (str): Chunk text. - token_count (int): Token count for the chunk. - fallback_count (bool): Recompute token count when provided count is falsy. Defaults to False. - """ - count = token_count or ( - self.count_tokens(text) if fallback_count else token_count - ) - chunks.append( - DocumentChunk( - text=text, - token_count=count, - index=len(chunks), - ) - ) - - -__all__ = ["DocumentChunk", "LLMProvider", "LLMResponse"] From 0c478a905ebef556e424efec7aa1bd298e6913c4 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 09:04:30 -0300 Subject: [PATCH 048/101] =?UTF-8?q?=F0=9F=A6=96=20Changed=20aymurai/settin?= =?UTF-8?q?gs.py=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/settings.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/aymurai/settings.py b/aymurai/settings.py index 85fe08e4..51036fe8 100644 --- a/aymurai/settings.py +++ b/aymurai/settings.py @@ -67,15 +67,6 @@ def assemble_cors_origins(cls, v) -> list[str]: # Fuzzy Matching THRESHOLD: int = 70 - # LLM - MODEL: str = "phi4:14b" - MODEL_CONTEXT: int = 9500 - TEMPERATURE: float = 0.0 - CONTEXT_WINDOW_LENGTH: int | None = 120 - TOKEN_LIMIT_FRAC: float = 2 / 3 - TOKENIZER_MODEL: str = "microsoft/phi-4" - DECOMPOSE_BY: int | None = None - # Label policies (JSON dict: label -> {disambiguation, anonymize}) DISAMBIGUATION_LABEL_POLICIES: dict | None = None From 0990214341968f1929d3402f6ff1070c3566eb64 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 15:47:35 -0300 Subject: [PATCH 049/101] =?UTF-8?q?=F0=9F=A6=96=20Changed=20aymurai/api/en?= =?UTF-8?q?dpoints/routers/anonymizer/anonymizer.py=20for=20release/v1.5.0?= =?UTF-8?q?=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🦖 Changed aymurai/utils/entity_disambiguation/__init__.py for release/v1.5.0 compatibility 🔥 Removed aymurai/utils/entity_disambiguation/llm.py for release/v1.5.0 compatibility --- .../routers/anonymizer/anonymizer.py | 7 +- .../utils/entity_disambiguation/__init__.py | 7 +- aymurai/utils/entity_disambiguation/llm.py | 379 ------------------ 3 files changed, 2 insertions(+), 391 deletions(-) delete mode 100644 aymurai/utils/entity_disambiguation/llm.py diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 5b3690bd..c90da84c 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -37,7 +37,6 @@ from aymurai.utils.entity_disambiguation import ( build_canonical_entities, get_canonical_dates, - load_prompts_from_yaml, map_canonical_entities_ner_preds, ) from aymurai.utils.misc import get_element @@ -396,8 +395,6 @@ async def anonymizer_disambiguate( effective_label_policies = _merge_label_policies(label_policies) logger.info("disambiguation labels: %d", len(labels)) - prompt_library = load_prompts_from_yaml() - all_detected_labels = { label.attrs.aymurai_label for label in labels @@ -407,9 +404,7 @@ async def anonymizer_disambiguate( and effective_label_policies.get(label.attrs.aymurai_label).anonymize } - default_llm_labels = ( - target_labels if target_labels else list(prompt_library.as_dict.keys()) - ) + default_llm_labels = target_labels if target_labels else [] llm_labels: list[str] = [] fuzzy_labels: set[str] = set() diff --git a/aymurai/utils/entity_disambiguation/__init__.py b/aymurai/utils/entity_disambiguation/__init__.py index 0794d1b3..1cfebd3d 100644 --- a/aymurai/utils/entity_disambiguation/__init__.py +++ b/aymurai/utils/entity_disambiguation/__init__.py @@ -5,17 +5,12 @@ from aymurai.utils.entity_disambiguation.fuzzy import ( build_canonical_entities, ) -from aymurai.utils.entity_disambiguation.llm import ( - llm_canonical_entities_inference, - load_prompts_from_yaml, -) + from aymurai.utils.entity_disambiguation.date_formatter import get_canonical_dates __all__ = [ "assign_label_instances", "build_canonical_entities", - "llm_canonical_entities_inference", "map_canonical_entities_ner_preds", - "load_prompts_from_yaml", "get_canonical_dates", ] diff --git a/aymurai/utils/entity_disambiguation/llm.py b/aymurai/utils/entity_disambiguation/llm.py deleted file mode 100644 index 6121d40c..00000000 --- a/aymurai/utils/entity_disambiguation/llm.py +++ /dev/null @@ -1,379 +0,0 @@ -import copy -import json -from functools import lru_cache -from pathlib import Path - -from more_itertools import unique_everseen -from transformers import AutoTokenizer - -from aymurai.llm_providers import OllamaLLMProvider -from aymurai.meta.api_interfaces import ( - DocLabel, - DocumentAnnotations, - PromptLibrary, - PromptSet, -) -from aymurai.meta.entities import CanonicalEntities, CanonicalEntity -from aymurai.settings import settings -from aymurai.utils.json_data import get_pretty -from aymurai.utils.yaml_data import load_yaml - - -@lru_cache(maxsize=1) -def load_prompts_from_yaml(): - """ - Loads and caches LLM prompt templates from the resources directory. - - Returns: - PromptLibrary: The loaded prompt library. - """ - path = Path(settings.RESOURCES_BASEPATH) / "llm/entity_disambiguation.yml" - data = load_yaml(file_path=str(path)) - - prompts = [] - for label, content in data.items(): - prompts.append( - PromptSet( - label=label, - system=content.get("system", ""), - user=content.get("user", ""), - ) - ) - return PromptLibrary(root=prompts) - - -def _extract_snippet(doc_text: str, label: DocLabel, window_length: int | None) -> str: - """ - Extracts and normalizes a text snippet around a label span. - - Args: - doc_text (str): Full document text. - label (DocLabel): Label containing span offsets. - window_length (int | None): Number of characters to include around - the label. If None, returns the full document text. - - Returns: - str: The extracted and normalized snippet. - """ - - if window_length is None: - return " ".join(doc_text.split()) - - start, end = label.start_char, label.end_char - window_start = max(0, start - window_length) - window_end = min(len(doc_text), end + window_length) - - # Expand to word boundaries to avoid truncating leading/trailing words. - while window_start > 0 and not doc_text[window_start - 1].isspace(): - window_start -= 1 - while window_end < len(doc_text) and not doc_text[window_end - 1].isspace(): - window_end += 1 - - snippet = doc_text[window_start:window_end] - return " ".join(snippet.split()) - - -def _iter_alias_snippets( - predictions: DocumentAnnotations, - *, - entity_label: str, - aliases_set: set[str], - window_length: int | None, -): - """ - Yields (alias, snippet) pairs for labels matching the target entity label. - - Args: - predictions (DocumentAnnotations): Document annotations to scan. - entity_label (str): Target NER label to match. - aliases_set (set[str]): Alias set to filter matches. - window_length (int | None): Window length for snippet extraction. - - Yields: - tuple[str, str]: (alias, snippet) pairs in appearance order. - """ - for pred in predictions: - if not pred.labels: - continue - doc_text = getattr(pred, "document", "") - - for label in pred.labels: - if label.attrs.aymurai_label != entity_label: - continue - - alias = label.attrs.aymurai_alt_text or label.text - if alias not in aliases_set: - continue - - snippet = _extract_snippet(doc_text, label, window_length) - yield alias, snippet - - -def _collect_entity_context( - predictions: DocumentAnnotations, - entity_label: str, - aliases: list[str], - window_length: int | None, - max_context_snippets: int | None = 5, -) -> list[str]: - """ - Collects unique context snippets for aliases, preserving appearance order. - - Args: - predictions (DocumentAnnotations): Document annotations to search within. - entity_label (str): NER label category to match (e.g., "PER"). - aliases (list[str]): Aliases to match against label text. - window_length (int | None): Characters to include around each match. - max_context_snippets (int | None): Max number of snippets to return. - If None, returns all unique snippets. Defaults to 5. - - Returns: - list[str]: Ordered, deduplicated context snippets. - """ - aliases_set = set(aliases) - - deduped_in_order = list( - unique_everseen( - _iter_alias_snippets( - predictions, - entity_label=entity_label, - aliases_set=aliases_set, - window_length=window_length, - ), - key=lambda item: item[1], - ) - ) - - # baseline in appearance order: first snippet per alias - seen_aliases: set[str] = set() - baseline = [ - snippet - for alias, snippet in deduped_in_order - if not (alias in seen_aliases or seen_aliases.add(alias)) - ] - - # remaining snippets in appearance order - baseline_set = set(baseline) - remaining = [ - snippet for _, snippet in deduped_in_order if snippet not in baseline_set - ] - - merged = baseline + remaining - - effective_limit = ( - None - if max_context_snippets is None - else max(max_context_snippets, len(baseline)) - ) - - return merged if effective_limit is None else merged[:effective_limit] - - -def _add_canonical_entities_context( - predictions: DocumentAnnotations, - entities: CanonicalEntities, - context_window_length: int | None = 120, - target_label: str | None = None, - max_snippets_per_entity: int | None = 5, -) -> CanonicalEntities: - """ - Attaches context snippets to each canonical entity based on predictions. - - Args: - predictions (DocumentAnnotations): Full document annotations. - entities (CanonicalEntities): Canonical entities to enrich with context. - context_window_length (int | None): Characters to include around each - mention. If None, uses the full document text. - target_label (str | None): Optional label filter to restrict extraction. - max_snippets_per_entity (int | None): Max snippets per entity. If None, - returns all unique snippets. - - Returns: - CanonicalEntities: Entities enriched with context snippets in attributes. - """ - - entities_with_context = copy.deepcopy(entities) - - for entity in entities_with_context: - if entity.attributes is None: - entity.attributes = {} - - current_target = target_label or entity.aymurai_label - - entity.attributes["context"] = _collect_entity_context( - predictions, - current_target, - list(entity.aliases), - context_window_length, - max_snippets_per_entity, - ) - - return entities_with_context - - -_TOKENIZER_CACHE = {} - - -def _get_model_tokens( - system_prompt: str, user_prompt: str, tokenizer_model: str -) -> int: - """ - Estimates token count for a system/user prompt using a HuggingFace tokenizer. - - Args: - system_prompt (str): System role prompt. - user_prompt (str): User role prompt. - tokenizer_model (str): HuggingFace tokenizer identifier or local path. - - Returns: - int: Approximate token count for the combined prompts. - """ - try: - if tokenizer_model not in _TOKENIZER_CACHE: - _TOKENIZER_CACHE[tokenizer_model] = AutoTokenizer.from_pretrained( - tokenizer_model - ) - - tokenizer = _TOKENIZER_CACHE[tokenizer_model] - - messages = [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_prompt}, - ] - - full_text = tokenizer.apply_chat_template(messages, tokenize=False) - tokens = tokenizer.encode(full_text) - - return len(tokens) - - except Exception: - # Fallback: simple word count approximation (one word ~ 1.4 tokens) - combined_text = f"{system_prompt} {user_prompt}" - word_count = len(combined_text.split()) - - approx_tokens = int(word_count * 1.4) + 20 - - return approx_tokens - - -def llm_canonical_entities_inference( - paragraphs: DocumentAnnotations, - canonical_entities_pre_cluster: CanonicalEntities, - system_prompt: str, - user_prompt_template: str, - model: str, - tokenizer_model: str, - target_label: str, - context_window_length: int | None = 120, - model_context: int = 9_500, - token_limit_frac: float = 2 / 3, - temperature: int = 0, - decompose_by: int | None = 0, -) -> CanonicalEntities: - """ - Refines pre-clustered entities into canonical forms using LLM inference. - - Args: - paragraphs (DocumentAnnotations): Annotated paragraphs containing labels. - canonical_entities_pre_cluster (CanonicalEntities): Pre-clustered entities. - system_prompt (str): System prompt for the LLM. - user_prompt_template (str): Template for user prompt formatting. - model (str): LLM model identifier. - tokenizer_model (str): Tokenizer identifier for token counting. - target_label (str): NER label to process (e.g., "PER"). - context_window_length (int | None): Context window length around mentions. - model_context (int): Max context window size for the LLM. - token_limit_frac (float): Fraction of model_context to use for batching. - temperature (int): Sampling temperature. - decompose_by (int | None): Batch size; if None, inferred. - - Returns: - CanonicalEntities: Canonical entities refined by the LLM. - """ - - canonical_entities_with_context = _add_canonical_entities_context( - predictions=paragraphs, - entities=canonical_entities_pre_cluster, - context_window_length=context_window_length, - target_label=target_label, - ) - - canonical_entities_prompt = [ - ce.model_dump(include={"canonical_text", "aliases", "attributes"}) - for ce in canonical_entities_with_context - ] - - token_limit = int(model_context * token_limit_frac) - - if decompose_by is None: - # NOTE: dynamically find the maximum batch size that fits the token limit - # by iteratively shrinking the candidate list. - current_batch_size = len(canonical_entities_prompt) - - while current_batch_size > 0: - test_batch = canonical_entities_prompt[:current_batch_size] - test_prompt = user_prompt_template.format( - canonical_entities=get_pretty(test_batch) - ) - - num_tokens = _get_model_tokens( - system_prompt=system_prompt, - user_prompt=test_prompt, - tokenizer_model=tokenizer_model, - ) - - if num_tokens <= token_limit: - decompose_by = current_batch_size - break - - current_batch_size -= 1 - - if not decompose_by: - # NOTE: returns None if even a single entity exceeds the token limit. - return None - - if decompose_by <= 0: - entity_batches = [canonical_entities_prompt] - else: - entity_batches = [ - canonical_entities_prompt[i : i + decompose_by] - for i in range(0, len(canonical_entities_prompt), decompose_by) - ] - - all_raw_outputs = [] - all_user_prompts = [] - - for batch_index, batch in enumerate(entity_batches): - user_prompt = user_prompt_template.format( - canonical_entities=get_pretty(batch), - ) - - all_user_prompts.append(user_prompt) - - len_tokens = _get_model_tokens( - system_prompt=system_prompt, - user_prompt=user_prompt, - tokenizer_model=tokenizer_model, - ) - if len_tokens > model_context: - # NOTE: skip batches that exceed physical context window to prevent LLM hallucination or crash. - continue - - provider = OllamaLLMProvider(model=model) - response = provider.generate( - messages=[ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_prompt}, - ], - options={"temperature": temperature, "num_ctx": model_context}, - format=CanonicalEntities.model_json_schema(), - ) - - batch_entities = json.loads(response.text).get("canonical_entities", []) - all_raw_outputs.extend(batch_entities) - - canonical_entities_llm = [ - CanonicalEntity.model_validate(e) for e in all_raw_outputs - ] - - return canonical_entities_llm From b05b768a189e73ac83b389838367ae38ae0645b5 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 15:52:52 -0300 Subject: [PATCH 050/101] =?UTF-8?q?=E2=8F=AA=20Reverted=20docker-compose.y?= =?UTF-8?q?ml=20to=205b9c220?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 205 +++++---------------------------------------- 1 file changed, 19 insertions(+), 186 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index eb8d8fe0..3ce7c536 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,196 +1,29 @@ -x-api-lite: &api-lite - image: ghcr.io/aymurai/api:latest - build: - context: . - dockerfile: ./docker/api/Dockerfile - target: aymurai-api - shm_size: "2gb" - ports: - - "8899:8899" - volumes: - - ./resources/cache:/resources/cache - env_file: - - .env - - .env.common - -x-api-full: &api-full - image: ghcr.io/aymurai/api:full - build: - context: . - dockerfile: ./docker/api/Dockerfile - target: aymurai-api-full - shm_size: "2gb" - ports: - - "8899:8899" - restart: always - -x-ollama: &ollama-base - image: ollama/ollama - ports: - - "11434:11434" - volumes: - - ollama:/root/.ollama - restart: always - shm_size: "2gb" - services: - mlflow: - image: ghcr.io/mlflow/mlflow:latest - container_name: mlflow - ports: - - "5000:5000" - environment: - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} - MLFLOW_S3_ENDPOINT_URL: http://minio:9000 - MLFLOW_S3_IGNORE_TLS: ${MLFLOW_S3_IGNORE_TLS} - command: - [ - "/bin/sh", - "-c", - "pip install --no-cache-dir psycopg2-binary boto3 && mlflow server --host 0.0.0.0 --port 5000 --backend-store-uri postgresql://mlflow:mlflow@postgres:5432/mlflow --default-artifact-root ${MLFLOW_ARTIFACT_ROOT}", - ] - depends_on: - postgres: - condition: service_healthy - minio: - condition: service_started - healthcheck: - test: - [ - "CMD", - "python", - "-c", - "import urllib.request; urllib.request.urlopen('http://localhost:5000')", - ] - interval: 10s - timeout: 5s - retries: 6 - - postgres: - image: postgres:16 - container_name: postgres - ports: - - "5432:5432" - environment: - POSTGRES_USER: mlflow - POSTGRES_PASSWORD: mlflow - POSTGRES_DB: mlflow - volumes: - - postgres-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U mlflow"] - interval: 10s - timeout: 5s - retries: 6 - - minio: - image: minio/minio:latest - container_name: minio + aymurai-api: + image: ghcr.io/aymurai/api:latest ports: - - "9002:9000" - - "9001:9001" - environment: - MINIO_ROOT_USER: ${AWS_ACCESS_KEY_ID} - MINIO_ROOT_PASSWORD: ${AWS_SECRET_ACCESS_KEY} - command: ["server", "/data", "--console-address", ":9001"] + - "8899:8899" + build: + context: . + dockerfile: ./docker/api/Dockerfile + target: aymurai-api + env_file: + - .env + - .env.common volumes: - - minio-data:/data - # Healthcheck removed; minio image lacks wget and minio-mc already waits for readiness. + - ./resources/cache:/resources/cache - minio-mc: - image: minio/mc:latest - container_name: minio-mc - depends_on: - minio: - condition: service_started - entrypoint: - [ - "/bin/sh", - "-c", - "until (/usr/bin/mc alias set local http://minio:9000 ${AWS_ACCESS_KEY_ID} ${AWS_SECRET_ACCESS_KEY}) do sleep 1; done; /usr/bin/mc mb -p local/mlflow; /usr/bin/mc anonymous set none local/mlflow; exit 0;", - ] - - # CPU-friendly API - aymurai-api: - <<: *api-lite - container_name: aymurai-api - depends_on: - - ollama - environment: - OLLAMA_HOST: http://ollama:11434 - TORCH_DEVICE: cpu - - # GPU-enabled API - aymurai-api-gpu: - <<: *api-lite - container_name: aymurai-api-gpu - depends_on: - - ollama-gpu - environment: - OLLAMA_HOST: http://ollama-gpu:11434 - TORCH_DEVICE: cuda - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [ gpu ] - - # CPU-friendly Full API aymurai-api-full: - <<: *api-full - container_name: aymurai-api-full - depends_on: - - ollama - environment: - OLLAMA_HOST: http://ollama:11434 - TORCH_DEVICE: cpu - deploy: - resources: - limits: - memory: 4g - cpus: "4.0" - - # GPU-enabled Full API - aymurai-api-full-gpu: - <<: *api-full - container_name: aymurai-api-full-gpu - depends_on: - - ollama-gpu - environment: - OLLAMA_HOST: http://ollama-gpu:11434 - TORCH_DEVICE: cuda + image: ghcr.io/aymurai/api:full + ports: + - "8899:8899" + build: + context: . + dockerfile: ./docker/api/Dockerfile + target: aymurai-api-full + restart: always deploy: resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [ gpu ] limits: memory: 4g cpus: "4.0" - - # CPU-only Ollama (default) - ollama: - <<: *ollama-base - container_name: ollama - - # GPU-enabled Ollama (run instead on GPU hosts) - ollama-gpu: - <<: *ollama-base - container_name: ollama-gpu - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [ gpu ] - -volumes: - ollama: - postgres-data: - minio-data: From ebe414b5679966f60058bcffaafa2df00c3f8044 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 15:55:04 -0300 Subject: [PATCH 051/101] =?UTF-8?q?=E2=8F=AA=20Revert=20docker/api/Dockerf?= =?UTF-8?q?ile=20to=204196117?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/api/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index 2462d4ec..660e5a7e 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -33,6 +33,7 @@ RUN --mount=type=bind,source=.git,target=.git \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-editable + # ------------------- # Base API Stage # ------------------- @@ -74,7 +75,6 @@ COPY --from=builder --chown=app:app /app/.venv /app/.venv # Copy static resources COPY resources/api/static /resources/api/static COPY resources/pipelines /resources/pipelines -COPY resources/llm /resources/llm # Set working directory WORKDIR /app @@ -96,7 +96,6 @@ ENV TF_CPP_MIN_LOG_LEVEL=3 \ # Copy additional resources COPY ./resources/api resources/api COPY ./resources/pipelines/production resources/pipelines/production -COPY ./resources/llm resources/llm # Initialize application to download models RUN uv run python /app/.venv/lib/python3.10/site-packages/aymurai/api/main.py From d312eea5412affce89f19b625e808901eca5beed Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:13:27 -0300 Subject: [PATCH 052/101] =?UTF-8?q?=F0=9F=A6=96=20Changed=20docs/anonymiza?= =?UTF-8?q?tion/README.md=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/anonymization/README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/anonymization/README.md b/docs/anonymization/README.md index 9beee13d..19603da6 100644 --- a/docs/anonymization/README.md +++ b/docs/anonymization/README.md @@ -9,13 +9,11 @@ 1. `/misc/document-extract` recibe el DOCX/PDF y devuelve el payload estructurado (`document_id`, lista de párrafos) tal como hoy. 2. Se procesa cada párrafo con `/anonymizer/predict`, obteniendo los resultados de NER (`aymurai_label`, texto, offsets, contexto previo y posterior y otros atributos). -3. Se consolida la inferencia: agrupamos todas las menciones detectadas y armamos un paquete enriquecido con contexto circundante (párrafo u oraciones vecinas). -4. Módulo de fuzzy-matching cruza cada mención extraída para sugerir candidatos de `CanonicalEntities` (`canonical_text` y `aliases`). -5. El contexto del paso 3 más las sugerencias del paso 4 son insumo para el servicio de desambiguación (LLM); el modelo se encarga de validar los `CanonicalEntities` sugeridos o proponer nuevos, al tiempo que infiere atributos adicionales (rol procesal), devolviendo una lista refinada de `CanonicalEntities`. -6. Con esa lista se ejecuta el mapeo de `Entity` a `CanonicalEntity`: cada mención NER queda anotada con un `canonical_entity_id` y un `aymurai_label_instance` (índice entero por orden de aparición) y cualquier atributo inferido, generando la desambiguación de entidades. -7. El frontend consume esos outputs y muestra el texto original con dos niveles de revisión: etiquetas NER y asignación canónica. -8. Tras la validación manual, el cliente invoca `/anonymizer/anonymize-document`, que ahora utiliza políticas de render (`render_policy`) para aplicar reemplazos consistentes (etiqueta o subclase con sufijos opcionales) y generar el documento anonimizado final. -9. El documento anonimizado queda disponible para descarga y se registran en la base de datos las decisiones finales (etiquetas y canónicos). +3. Módulo de fuzzy-matching cruza cada mención extraída para sugerir candidatos de `CanonicalEntities` (`canonical_text` y `aliases`). +4. Con esa lista se ejecuta el mapeo de `Entity` a `CanonicalEntity`: cada mención NER queda anotada con un `canonical_entity_id` y un `aymurai_label_instance` (índice entero por orden de aparición) y cualquier atributo inferido, generando la desambiguación de entidades. +5. El frontend consume esos outputs y muestra el texto original con dos niveles de revisión: etiquetas NER y asignación canónica. +6. Tras la validación manual, el cliente invoca `/anonymizer/anonymize-document`, que ahora utiliza políticas de render (`render_policy`) para aplicar reemplazos consistentes (etiqueta o subclase con sufijos opcionales) y generar el documento anonimizado final. +7. El documento anonimizado queda disponible para descarga y se registran en la base de datos las decisiones finales (etiquetas y canónicos). ## Cambios principales en el modelo de datos @@ -72,9 +70,8 @@ El `render_policy` puede definirse por entorno (`RENDER_POLICY`) o por request y El objetivo general es optimizar la extracción y desambiguación de entidades. Para avanzar con criterio orientado a métricas mediremos cada capa y el sistema end-to-end. -- **NER**: aunque el modelo actual cubre el caso de uso, debemos planificar un nuevo ciclo de entrenamiento y finetuning. Será importante consolidar un dataset público anotado con las entidades a detectar, para lo cual podemos aprovechar repositorios públicos de documentos legales de Argentina (e idealmente, del resto de latinoamérica). Para el etiquetado automático, podemos combinar predicciones del NER actual con las extracciones obtenidas usando [`langextract`](/notebooks/experiments/anonymization/05-langextract.ipynb), y conservar solo las coincidencias exactas entre ambos sistemas para minimizar ruido. Luego, se deberán realizar algunas revisiones manuales para asegurar calidad. Este dataset nos permitirá definir un split de evaluación estable y automatizar el cálculo de F1 por etiqueta para comparar versiones de forma consistente. +- **NER**: aunque el modelo actual cubre el caso de uso, debemos planificar un nuevo ciclo de entrenamiento y finetuning. Será importante consolidar un dataset público anotado con las entidades a detectar, para lo cual podemos aprovechar repositorios públicos de documentos legales de Argentina (e idealmente, del resto de latinoamérica). Luego, se deberán realizar algunas revisiones manuales para asegurar calidad. Este dataset nos permitirá definir un split de evaluación estable y automatizar el cálculo de F1 por etiqueta para comparar versiones de forma consistente. - **Heurística de agrupación**: vamos a contrastar variantes como Levenshtein normalizada, token fuzzy matching o TF-IDF con coseno. Cada método se ejecutará sobre predicciones reales del NER (para medir robustez ante ruido) y sobre etiquetas existentes (para estimar el techo teórico). La métrica utilizada para evaluar será aquella definida en `/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md`, contra un conjunto de `CanonicalEntities` ya anotados. -- **LLM para desambiguación**: la calidad depende de cómo se promptea y del contexto entregado. Deberíamos experimentar con distintas ventanas de contexto variable (documento completo, párrafos que contienen entidades, oraciones vecinas, metadata, etc.), variantes de prompt (instrucciones directas versus cadenas guiadas) y configuraciones de temperatura/top-p. Cada variante se evaluará con la métrica anteriormente mencionada. Finalmente consolidaremos una evaluación integrada combinando el mejor NER disponible, la heurística con mayor cobertura y baja tasa de falsos positivos y la versión de prompt más precisa. El benchmark se ejecutará sobre un conjunto de documentos de validación con etiquetas canónicas revisadas. From dde8f0e89e1c9d56d7e39aa2cbfe577d2603ea2a Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:17:26 -0300 Subject: [PATCH 053/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20docs/experimen?= =?UTF-8?q?ts/README.md=20for=20realease/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔥 Removed docs/experiments/base.yaml for realease/v1.5.0 compatibility --- docs/experiments/README.md | 90 -------------------------------------- docs/experiments/base.yaml | 50 --------------------- 2 files changed, 140 deletions(-) delete mode 100644 docs/experiments/README.md delete mode 100644 docs/experiments/base.yaml diff --git a/docs/experiments/README.md b/docs/experiments/README.md deleted file mode 100644 index 87c1f771..00000000 --- a/docs/experiments/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Experiments Setup - -This guide walks through running the entity-disambiguation experiments with MLflow -tracking and MinIO artifacts. - -## 1) Update the devcontainer - -The devcontainer installs base dependencies via `uv`. The MLflow client lives in the -`mlops` dependency group, so install it after rebuilding the container: - -```bash -uv sync --frozen --all-extras --group mlops -``` - -If you use VS Code, run "Dev Containers: Rebuild and Reopen in Container" first. - -## 2) Configure environment - -- Set MinIO credentials in `.env`: - - `AWS_ACCESS_KEY_ID` - - `AWS_SECRET_ACCESS_KEY` -- Verify MLflow settings in `.env.common`: - - `MLFLOW_TRACKING_URI` - - `MLFLOW_S3_ENDPOINT_URL` - - `MLFLOW_ARTIFACT_ROOT` - -The experiment runner also uses: -- `API_BASE_URL` (defaults to `http://localhost:8899`) -- `REQUEST_TIMEOUT` (defaults to `300`) - -## 3) Start services - -```bash -make mlops-up -make ollama-up -make api-up -``` - -If you need the GPU services, set: -```bash -make ollama-up OLLAMA_SERVICE=ollama-gpu -make api-up API_SERVICE=aymurai-api-gpu -``` - -## 4) Pull the model - -```bash -make ollama-pull MODEL=gemma3:270m -``` - -## 5) Run the experiment - -```bash -make exp-run CONFIG=resources/experiments/entity-disambiguation/exp-gemma3-pv1.yaml -``` - -## Predictions vs. ground truth filenames - -Ground-truth files are expected to end with `-test.json`, while predictions use -the same base name without the suffix. - -Example: -- `document-1-test.json` (ground truth) -- `document-1.json` (prediction) - -Predictions are written to disk (not logged as artifacts). MLflow logs: -- Run params (model, prompt IDs, dataset hash, etc.) -- Aggregate metrics and per-document scores (as an artifact) -- Prompt text (if enabled in config) - -## 6) Access MLflow UI - -If running remotely, forward the MLflow port: -```bash -ssh -L 5000:127.0.0.1:5000 user@remote-host -``` - -Then open `http://localhost:5000`. - -## Config location - -Experiment configs live under: -- `resources/experiments/entity-disambiguation/` - -Use the template at `docs/experiments/base.yaml` when creating new experiment -configs. Copy it into `resources/experiments/entity-disambiguation/` and adjust -the model, prompts, and data paths as needed. - -The main runner is: -- `aymurai/experiments/entity_disambiguation/runner.py` diff --git a/docs/experiments/base.yaml b/docs/experiments/base.yaml deleted file mode 100644 index a3fb6adc..00000000 --- a/docs/experiments/base.yaml +++ /dev/null @@ -1,50 +0,0 @@ -experiment: - name: "entity-disambiguation" - run_name: "{model}-{system_id}-{user_id}-{timestamp}" - -model: - provider: "ollama" - name: "llama3:8b" - temperature: 0.2 - max_tokens: 512 - -prompts: - system: - id: "sys-001" - text: "" - user: - id: "user-001" - text: "" - - -data: - ground_truth_dir: "/data/gt" - input_dir: "/data/input" - dataset_id: "gt-v1" - manifest: - enabled: true - mode: "metadata_only" - -predictions: - output_base_dir: "/data/preds" - dir_name_template: "preds-{model}-{system_id}-{user_id}-{timestamp}" - -logging: - mlflow: - experiment_name: "entity-disambiguation" - privacy: - log_prompt_text: true - log_data_manifest: false - log_per_doc_scores: true - per_doc_scores: - id_strategy: "anon_filename" - -evaluation: - function: "evaluate_disambiguation" - weights: - w_ent: 0.4 - w_alias: 0.35 - w_label: 0.2 - w_role: 0.05 - per_doc_scores: true - normalize: true From d56007591325c0153133f494835cf4a0270d180e Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:20:26 -0300 Subject: [PATCH 054/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20notebooks/expe?= =?UTF-8?q?riments/anonymization/05-langextract.ipynb=20for=20release/v1.5?= =?UTF-8?q?.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../anonymization/05-langextract.ipynb | 353 ------------------ 1 file changed, 353 deletions(-) delete mode 100644 notebooks/experiments/anonymization/05-langextract.ipynb diff --git a/notebooks/experiments/anonymization/05-langextract.ipynb b/notebooks/experiments/anonymization/05-langextract.ipynb deleted file mode 100644 index df2b0646..00000000 --- a/notebooks/experiments/anonymization/05-langextract.ipynb +++ /dev/null @@ -1,353 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "7052227b", - "metadata": {}, - "outputs": [], - "source": [ - "import langextract as lx\n", - "from rich.pretty import pprint" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "01ef2998", - "metadata": {}, - "outputs": [], - "source": [ - "# 1. Define the prompt and extraction rules\n", - "prompt = \"\"\"\n", - " Sos un asistente especializado en el análisis de documentos judiciales.\n", - " Tu tarea es identificar y extraer menciones de información sensible para su posterior anonimización.\n", - " Debés detectar fragmentos textuales que correspondan a cualquiera de las siguientes entidades:\n", - "\n", - " - \"BANCO\": Nombre de una entidad bancaria, pública o privada.\n", - " - \"CBU\": Código Bancario Uniforme (22 dígitos) de una cuenta.\n", - " - \"CORREO_ELECTRONICO\": Dirección de correo electrónico.\n", - " - \"CUIJ\": Código Único de Identificación Jurídica de causas judiciales.\n", - " - \"CUIT_CUIL\": Número de CUIT o CUIL de una persona física o jurídica.\n", - " - \"DIRECCION\": Dirección postal específica (calle, número, etc.).\n", - " - \"DNI\": Número de Documento Nacional de Identidad u otro documento identificatorio.\n", - " - \"EDAD\": Edad explícita de una persona.\n", - " - \"ESTUDIOS\": Nivel o institución educativa que permita identificar a la persona (ej. \"primario incompleto\", \"secundario completo\", \"Licenciado en…\").\n", - " - \"FECHA\": Fecha completa o parcial (día, mes y/o año).\n", - " - \"LINK\": Enlace o URL a una página web.\n", - " - \"LOC\": Localización geográfica específica (ciudad, barrio, comisaría, etc.).\n", - " - \"MARCA_AUTOMOVIL\": Marca de un vehículo (ej. Toyota, Ford).\n", - " - \"NACIONALIDAD\": Nacionalidad de una persona (ej. \"argentino\", \"brasileña\").\n", - " - \"NUM_ACTUACION\": Número identificatorio de una actuación administrativa o contravencional.\n", - " - \"NUM_CAJA_AHORRO\": Número completo de una caja de ahorro o cuenta bancaria.\n", - " - \"NUM_EXPEDIENTE\": Número de expediente judicial o administrativo.\n", - " - \"NUM_MATRICULA\": Número de matrícula profesional o académica.\n", - " - \"PATENTE_DOMINIO\": Patente o dominio de un vehículo.\n", - " - \"PER\": Nombre y apellido(s) de una persona física. Los nombres inicializados y los apodos también cuentan como información sensible a anonimizar.\n", - " - \"TELEFONO\": Número telefónico (fijo o celular).\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "96f85b5f", - "metadata": {}, - "outputs": [], - "source": [ - "# 2. Provide a high-quality example to guide the model\n", - "examples = [\n", - " lx.data.ExampleData(\n", - " text=\"El 5 de mayo de 2023 el señor Fiscal indicó que realizó distintas medidas de prueba y que del resultado surge que tanto la investigada como el menor Juan Pérez se domicilian en la calle Sarmiento 1234, de la localidad de Moreno, por lo que solicitó que se declare la incompetencia en razón del territorio y se envíe el caso al Juzgado de Garantías que corresponda del Departamento Judicial de Moreno, con jurisdicción en el partido de Moreno.\",\n", - " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"FECHA\", extraction_text=\"5 de mayo de 2023\"\n", - " ),\n", - " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Juan Pérez\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"DIRECCION\", extraction_text=\"Sarmiento 1234\"\n", - " ),\n", - " lx.data.Extraction(extraction_class=\"LOC\", extraction_text=\"Moreno\"),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"JUZGADO DE 1RA INSTANCIA EN LO PENAL CONTRAVENCIONAL Y DE FALTAS N°10 SECRETARIA N°19\\nCarlos Gómez sobre 84 - HOMICIDIO CULPOSO Y OTROS\\nNúmero: 52345/2022\\nCUIJ: 12-34567890-1\\nActuación Nro: 2022-009876\",\n", - " extractions=[\n", - " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Carlos Gómez\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"NUM_EXPEDIENTE\", extraction_text=\"52345/2022\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"CUIJ\", extraction_text=\"12-34567890-1\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"NUM_ACTUACION\", extraction_text=\"2022-009876\"\n", - " ),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"Acusado: Miguel Torres, DNI 30123456, nacido el 14/02/1990, de 34 años de edad, de nacionalidad paraguaya, género varón cis, con estudios secundarios completos, hizo hasta 3er año porque fue padre joven, con último domicilio en Av. Corrientes 3456, de esta ciudad, donde vive con su hermana y su cuñado Jorge Pérez. Tiene dos hijos a su cargo, de 5 y 8 años. Su hijo de 8 vive con él, su hija de 5 vive con su madre, Laura Fernández.\",\n", - " extractions=[\n", - " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Miguel Torres\"),\n", - " lx.data.Extraction(extraction_class=\"DNI\", extraction_text=\"30123456\"),\n", - " lx.data.Extraction(extraction_class=\"FECHA\", extraction_text=\"14/02/1990\"),\n", - " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"34\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"NACIONALIDAD\", extraction_text=\"paraguaya\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"ESTUDIOS\",\n", - " extraction_text=\"estudios secundarios completos\",\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"DIRECCION\",\n", - " extraction_text=\"Av. Corrientes 3456\",\n", - " ),\n", - " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Jorge Pérez\"),\n", - " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"5\"),\n", - " lx.data.Extraction(extraction_class=\"EDAD\", extraction_text=\"8\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Laura Fernández\"\n", - " ),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"El testigo Juan López dejó asentado su número de contacto: 11-2345-6789. Indicó que la médica Dra. Ana García, MN 12345, asistió al lugar donde se hallaba un vehículo Volkswagen, patente AB123CD.\",\n", - " extractions=[\n", - " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Juan López\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"TELEFONO\", extraction_text=\"11-2345-6789\"\n", - " ),\n", - " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Ana García\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"NUM_MATRICULA\", extraction_text=\"12345\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"MARCA_AUTOMOVIL\",\n", - " extraction_text=\"Volkswagen\",\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"PATENTE_DOMINIO\", extraction_text=\"AB123CD\"\n", - " ),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"Se identificó una transferencia bancaria con los siguientes datos: CUIT 20-12345678-3, CBU 2850590940090412345671, Caja de Ahorro N° 12345678, Banco Nación.\",\n", - " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"CUIT_CUIL\", extraction_text=\"20-12345678-3\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"CBU\",\n", - " extraction_text=\"2850590940090412345671\",\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"NUM_CAJA_AHORRO\", extraction_text=\"12345678\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"BANCO\", extraction_text=\"Banco Nación\"\n", - " ),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"Para mayor información, comunicarse a fiscalia.central@justicia.gob.ar o visitar el sitio https://justicia.gob.ar/actuaciones.\",\n", - " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"CORREO_ELECTRONICO\",\n", - " extraction_text=\"fiscalia.central@justicia.gob.ar\",\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"LINK\",\n", - " extraction_text=\"https://justicia.gob.ar/actuaciones\",\n", - " ),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"En la Ciudad Autónoma de Buenos Aires, el día 5 de mayo de 2023, el Sr. Fiscal hace saber que Juan Pérez se domicilia en la calle Sarmiento 1234, localidad de Moreno.\",\n", - " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"FECHA\", extraction_text=\"5 de mayo de 2023\"\n", - " ),\n", - " lx.data.Extraction(extraction_class=\"PER\", extraction_text=\"Juan Pérez\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"DIRECCION\", extraction_text=\"Sarmiento 1234\"\n", - " ),\n", - " lx.data.Extraction(extraction_class=\"LOC\", extraction_text=\"Moreno\"),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"Por otra parte, la División Investigaciones Judiciales de la Policía Federal Argentina informó que no se dio intervención a ninguna otra Fiscalía u otro Juzgado por la sustracción del vehículo Volkswagen Voyage, dominio KXY-876\",\n", - " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"PATENTE_DOMINIO\", extraction_text=\"KXY-876\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"MARCA_AUTOMOVIL\",\n", - " extraction_text=\"Volkswagen Voyage\",\n", - " ),\n", - " ],\n", - " ),\n", - " lx.data.ExampleData(\n", - " text=\"A su vez, requirió informes al Banco BBVA Francés, respecto de las cuentas bancarias de la denunciante, Carla Alejandra Garcia, D.N.I. 36.998.621 identificadas como Caja de ahorro en pesos argentinos número 117-59824/6 con CBU 0180132640000004685591 y Caja de ahorro en dólares número 119-619018/2 con CBU 0170115544000062081822.\",\n", - " extractions=[\n", - " lx.data.Extraction(\n", - " extraction_class=\"PER\", extraction_text=\"Carla Alejandra Garcia\"\n", - " ),\n", - " lx.data.Extraction(extraction_class=\"DNI\", extraction_text=\"36.998.621\"),\n", - " lx.data.Extraction(\n", - " extraction_class=\"NUM_CAJA_AHORRO\", extraction_text=\"117-59824/6\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"CBU\", extraction_text=\"0180132640000004685591\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"NUM_CAJA_AHORRO\", extraction_text=\"119-619018/2\"\n", - " ),\n", - " lx.data.Extraction(\n", - " extraction_class=\"CBU\", extraction_text=\"0170115544000062081822\"\n", - " ),\n", - " ],\n", - " ),\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb82c82d", - "metadata": {}, - "outputs": [], - "source": [ - "len(examples)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c52e56f4", - "metadata": {}, - "outputs": [], - "source": [ - "iter_examples = iter(examples)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "68945d23", - "metadata": {}, - "outputs": [], - "source": [ - "example = next(iter_examples)\n", - "pprint(example)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a34c40a8", - "metadata": {}, - "outputs": [], - "source": [ - "# Sample text to extract from\n", - "text = \"La Fiscalía determinó que el objeto de este caso es investigar el hecho que tuvo lugar el día 12 de marzo de 2023 a las 8:50 horas aproximadamente, ocasión en que Carlos Gómez y María Rodriguez estafaron a Juan Pérez por un monto total de pesos treinta y tres mil novecientos sesenta ($33.960).\"\n", - "\n", - "# Run the extraction\n", - "result = lx.extract(\n", - " text_or_documents=text,\n", - " prompt_description=prompt,\n", - " examples=examples,\n", - " model_id=\"llama3.1:8b\",\n", - " model_url=\"http://localhost:11434\",\n", - " max_char_buffer=16_384,\n", - " fence_output=False,\n", - " use_schema_constraints=False,\n", - ")\n", - "\n", - "pprint(result)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9064a3b6", - "metadata": {}, - "outputs": [], - "source": [ - "# Custom OpenAI model integration\n", - "# Support for Ollama gpt-oss:20b #116\n", - "# https://github.com/google/langextract/issues/116#issuecomment-3209618602\n", - "from langextract.providers.openai import OpenAILanguageModel\n", - "\n", - "\n", - "# Custom OpenAI model class to handle Ollama gpt-oss:20b\n", - "class CustomOpenAIModel(OpenAILanguageModel):\n", - " def _process_single_prompt(self, prompt, config):\n", - " api_params = {\n", - " \"model\": self.model_id,\n", - " \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n", - " \"temperature\": 0.1,\n", - " \"max_tokens\": 2000,\n", - " }\n", - "\n", - " try:\n", - " response = self._client.chat.completions.create(**api_params)\n", - " output_text = response.choices[0].message.content\n", - " return lx.inference.ScoredOutput(score=1.0, output=output_text)\n", - "\n", - " except Exception as e:\n", - " raise lx.exceptions.InferenceRuntimeError(\n", - " f\"Custom OpenAI API error: {str(e)}\", original=e\n", - " ) from e\n", - "\n", - "\n", - "# Instantiate the custom model\n", - "custom_model = CustomOpenAIModel(\n", - " model_id=\"gpt-oss:20b\",\n", - " api_key=\"dummy\",\n", - " base_url=\"http://localhost:11434/v1\",\n", - ")\n", - "\n", - "# Run extraction with the custom model\n", - "result = lx.extract(\n", - " text_or_documents=text,\n", - " prompt_description=prompt,\n", - " examples=examples,\n", - " model=custom_model,\n", - " fence_output=False,\n", - " use_schema_constraints=False,\n", - ")\n", - "pprint(result)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e905e794", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai (3.10.19)", - "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.10.19" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From d35c6cb53ccce0130e5dd35f91ca5ed2038cd17b Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:31:36 -0300 Subject: [PATCH 055/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20all=20the=20?= =?UTF-8?q?=20notebooks=20from=20folder:=20notebooks/experiments/entity-di?= =?UTF-8?q?sambiguation=20that=20had=20something=20related=20to=20LLM=20di?= =?UTF-8?q?sambiguation=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../01-entity-disambiguation.ipynb | 506 ---- .../02-disambiguation-metric.ipynb | 431 --- ...ation-from-pre-clustered-validations.ipynb | 899 ------ ...tion-pre-clustered-manual-validation.ipynb | 2447 ----------------- .../07-LLM-entities-disambiguation.ipynb | 1713 ------------ .../08-disambiguation-endpoint-smoke.ipynb | 399 --- 6 files changed, 6395 deletions(-) delete mode 100644 notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb delete mode 100644 notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb delete mode 100644 notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb delete mode 100644 notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb delete mode 100644 notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb delete mode 100644 notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb diff --git a/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb deleted file mode 100644 index d8ff91eb..00000000 --- a/notebooks/experiments/entity-disambiguation/01-entity-disambiguation.ipynb +++ /dev/null @@ -1,506 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c8fc128c", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext rich\n", - "\n", - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8eeefe8b", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import json\n", - "import mimetypes\n", - "import os\n", - "import re\n", - "import time\n", - "from pathlib import Path\n", - "from typing import Iterable\n", - "\n", - "import requests\n", - "from tqdm import tqdm\n", - "\n", - "from aymurai.llm_providers import OllamaLLMProvider\n", - "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", - "from aymurai.utils.json_data import get_pretty, save_json" - ] - }, - { - "cell_type": "markdown", - "id": "84d11bba", - "metadata": {}, - "source": [ - "## /document-extract endpoint output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e0ec450", - "metadata": {}, - "outputs": [], - "source": [ - "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8899\")\n", - "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", - "DATA_ROOT = Path(\n", - " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/disambiguation-eval\")\n", - ")\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", - "\n", - "print(f\"Target endpoint: {ENDPOINT}\")\n", - "print(f\"Data root: {DATA_ROOT.resolve()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef352767", - "metadata": {}, - "outputs": [], - "source": [ - "if not DATA_ROOT.exists():\n", - " raise FileNotFoundError(\n", - " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", - " )\n", - "\n", - "\n", - "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "\n", - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "print(f\"Discovered {len(documents)} documents.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "21fd6e36", - "metadata": {}, - "outputs": [], - "source": [ - "def call_extraction_api(\n", - " session: requests.Session, file_path: Path\n", - ") -> dict[str, object]:\n", - " payload: dict[str, object] = {\n", - " \"path\": str(file_path),\n", - " \"status\": \"failure\",\n", - " \"status_code\": None,\n", - " \"elapsed_s\": None,\n", - " \"detail\": None,\n", - " }\n", - "\n", - " if not file_path.exists():\n", - " payload[\"detail\"] = \"File does not exist\"\n", - " return payload\n", - "\n", - " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", - " files = {\n", - " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", - " }\n", - "\n", - " try:\n", - " start = time.perf_counter()\n", - " response = session.post(\n", - " ENDPOINT,\n", - " files=files,\n", - " timeout=REQUEST_TIMEOUT,\n", - " )\n", - " elapsed = time.perf_counter() - start\n", - " except requests.RequestException as exc:\n", - " payload[\"detail\"] = f\"Request failed: {exc}\"\n", - " return payload\n", - " finally:\n", - " files[\"file\"][1].close()\n", - "\n", - " payload[\"status_code\"] = response.status_code\n", - " payload[\"elapsed_s\"] = elapsed\n", - "\n", - " try:\n", - " response_body = response.json()\n", - " except ValueError:\n", - " response_body = {\"raw\": response.text[:500]}\n", - "\n", - " if response.ok:\n", - " payload[\"status\"] = \"success\"\n", - " payload[\"detail\"] = {\n", - " \"document_id\": response_body.get(\"document_id\"),\n", - " \"document\": response_body.get(\"document\", []),\n", - " }\n", - " else:\n", - " payload[\"detail\"] = response_body\n", - "\n", - " return payload" - ] - }, - { - "cell_type": "markdown", - "id": "9254ef68", - "metadata": {}, - "source": [ - "## Inference" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a308cf90", - "metadata": {}, - "outputs": [], - "source": [ - "# Function to make inference using the API\n", - "def get_predictions(sample: str) -> dict:\n", - " response = requests.post(\n", - " url=f\"{API_BASE_URL}/anonymizer/predict\", json={\"text\": sample}\n", - " )\n", - " response.raise_for_status()\n", - " return response.json()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7ef6a0fa", - "metadata": {}, - "outputs": [], - "source": [ - "from itertools import chain\n", - "from operator import itemgetter\n", - "from typing import Any\n", - "\n", - "from more_itertools import unique_everseen\n", - "\n", - "\n", - "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", - " \"\"\"\n", - " Parse prediction labels to extract unique aymurai_label and aymurai_alt_text pairs.\n", - "\n", - " Args:\n", - " predictions (list[dict[str, Any]]): A list of prediction dictionaries.\n", - "\n", - " Returns:\n", - " list[dict[str, str]]: A list of dictionaries containing unique aymurai_label and aymurai_alt_text pairs.\n", - " \"\"\"\n", - " attrs_stream = (\n", - " label.get(\"attrs\") or {}\n", - " for label in chain.from_iterable(pred.get(\"labels\", ()) for pred in predictions)\n", - " )\n", - "\n", - " unique_pairs = unique_everseen(\n", - " (\n", - " attrs.get(\"aymurai_label\"),\n", - " attrs.get(\"aymurai_alt_text\"),\n", - " )\n", - " for attrs in attrs_stream\n", - " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", - " )\n", - "\n", - " return sorted(\n", - " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", - " key=itemgetter(\"aymurai_label\", \"text\"),\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "ccbe21f4", - "metadata": {}, - "source": [ - "## CanonicalEntity extraction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd025617", - "metadata": {}, - "outputs": [], - "source": [ - "# Available models\n", - "# model = \"gpt-oss:20b\"\n", - "# model = \"llama3\"\n", - "model = \"phi4:14b\"\n", - "# model = \"llama3.1:8b\"\n", - "# model = \"gemma3:270m\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25284997", - "metadata": {}, - "outputs": [], - "source": [ - "# Sanity check\n", - "provider = OllamaLLMProvider(model=model)\n", - "provider.generate(\"hola, ¿cómo estás?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c7ec7c6", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt = \"\"\"\n", - "Eres un asistente especializado en anonimización de sentencias judiciales.\n", - "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", - "Una **entidad canónica** es la representación única de una entidad real.\n", - "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", - "\n", - "# Reglas\n", - "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", - "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", - "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", - "- Cada entidad canónica debe incluir:\n", - " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", - " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", - " - `aliases` (todas las menciones textuales relevantes).\n", - " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", - "\n", - "# Notas\n", - "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", - "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - "\n", - "# Ejemplo\n", - "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", - "A: ```json\n", - "[\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Laura Beatriz Gómez\",\n", - " \"aliases\": [\"Laura Beatriz Gómez\"],\n", - " \"attributes\": {\"role\": \"Denunciante\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"42987654\",\n", - " \"aliases\": [\"42.987.654\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DIRECCION\",\n", - " \"canonical_text\": \"calle Falsa 123\",\n", - " \"aliases\": [\"calle Falsa 123\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", - " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", - " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", - " \"attributes\": {\"role\": \"Denunciado\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"27654321\",\n", - " \"aliases\": [\"27.654.321\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"calle Real 789\",\n", - " \"aliases\": [\"calle Real 789\"]\n", - " }\n", - "]\n", - "```\n", - "\"\"\"\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6994e3c3", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template = \"\"\"\n", - "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", - "\n", - "# Documento\n", - "{document_text}\n", - "\n", - "# Menciones de entidades detectadas por el NER\n", - "{ner_output_json}\n", - "\n", - "# Instrucciones\n", - "1. Evalúa cada mención listada. Si representa una entidad real, inclúyela en la lista de aliases de alguna entidad canónica.\n", - "2. No omitas entidades válidas, alias con iniciales ni variantes abreviadas.\n", - "3. Solo fusiona menciones cuando exista evidencia clara de que se refieren a la misma entidad.\n", - "4. Normaliza canonical_text; mantén los alias tal como aparecen.\n", - "5. Utiliza attributes para indicar roles (p. ej. {{\"role\": \"Denunciante\"}}) o aclaraciones de desambiguación.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "349fa489", - "metadata": {}, - "outputs": [], - "source": [ - "def extract_canonical_entities(\n", - " doc_path: str, model: str = \"phi4:14b\"\n", - ") -> CanonicalEntities:\n", - " \"\"\"\n", - " Extract canonical entities from a document.\n", - "\n", - " Args:\n", - " doc_path (str): The path to the document.\n", - " model (str, optional): The model to use for extraction. Defaults to \"phi4:14b\".\n", - "\n", - " Raises:\n", - " ValueError: If the document is empty or not found.\n", - " ValueError: If the document summary is empty or not found.\n", - "\n", - " Returns:\n", - " CanonicalEntities: The extracted canonical entities.\n", - " \"\"\"\n", - " # Extract document\n", - " session = requests.Session()\n", - " document = call_extraction_api(session, Path(doc_path))\n", - " document = document.get(\"detail\", {}).get(\"document\")\n", - "\n", - " if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")\n", - "\n", - " # Get NER predictions\n", - " ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - " parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", - " ner_output_json = get_pretty(parsed_ner_labels)\n", - "\n", - " # Prepare user prompt\n", - " user_prompt = user_prompt_template.format(\n", - " document_text=\"\\n\".join(document).strip(),\n", - " ner_output_json=ner_output_json,\n", - " )\n", - "\n", - " # Get canonical entities from the model\n", - " provider = OllamaLLMProvider(model=model)\n", - " response = provider.generate(\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options={\"temperature\": 0},\n", - " format=CanonicalEntities.model_json_schema(),\n", - " )\n", - "\n", - " # Parse canonical entities from response\n", - " canonical_entities = [\n", - " output for output in json.loads(response.text)[\"canonical_entities\"]\n", - " ]\n", - "\n", - " canonical_entities = [\n", - " CanonicalEntity.model_validate(canonical_entity)\n", - " for canonical_entity in canonical_entities\n", - " ]\n", - "\n", - " return canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52c14551", - "metadata": {}, - "outputs": [], - "source": [ - "output_root = Path(DATA_ROOT) / \"preds\"\n", - "output_root.mkdir(parents=True, exist_ok=True)\n", - "\n", - "prompt_version = \"1\"\n", - "\n", - "timestamp = time.strftime(\"%y%m%d_%H%M\")\n", - "base_dir_name = f\"preds-{model}-pv{prompt_version}-{timestamp}\"\n", - "output_dir = output_root / base_dir_name\n", - "\n", - "output_dir.mkdir(parents=True, exist_ok=False)\n", - "print(f\"Saving canonical entities to {output_dir}\")\n", - "\n", - "for doc_path in documents:\n", - " print(f\"Processing document: {doc_path}\")\n", - "\n", - " try:\n", - " canonical_entities = extract_canonical_entities(doc_path, model=model)\n", - " print(f\"Extracted {len(canonical_entities)} canonical entities.\")\n", - "\n", - " target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", - " )\n", - " target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)\n", - " target_path = output_dir / f\"{target_filename}.json\"\n", - "\n", - " save_json(\n", - " [\n", - " canonical_entity.model_dump()\n", - " | {\"entity_id\": canonical_entity.entity_id.hex}\n", - " for canonical_entity in canonical_entities\n", - " ],\n", - " str(target_path),\n", - " )\n", - "\n", - " except Exception as e:\n", - " print(f\"Error processing document {doc_path}: {e}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f734b642", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai (3.10.19)", - "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.10.19" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb b/notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb deleted file mode 100644 index ebe84474..00000000 --- a/notebooks/experiments/entity-disambiguation/02-disambiguation-metric.ipynb +++ /dev/null @@ -1,431 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "10301fb9", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import json\n", - "import unicodedata\n", - "from pathlib import Path\n", - "from typing import Any, Dict, Iterable, List, Optional, Set, Tuple\n", - "\n", - "from IPython.display import Markdown\n", - "from ollama import ChatResponse\n", - "from pydantic import BaseModel\n", - "from pydantic.json_schema import JsonSchemaValue\n", - "from rich.pretty import pprint\n", - "from tqdm import tqdm\n", - "\n", - "from aymurai.meta.entities import CanonicalEntity # , EntityRelation\n", - "from aymurai.utils.json_data import get_pretty, load_json, save_json" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93f3977b", - "metadata": {}, - "outputs": [], - "source": [ - "def normalize_text(text: str) -> str:\n", - " \"\"\"\n", - " Normaliza un alias:\n", - " - pasa a minúsculas\n", - " - elimina espacios extra\n", - " - quita tildes/acentos\n", - " \"\"\"\n", - " if text is None:\n", - " return \"\"\n", - " text = text.strip().lower()\n", - " # eliminar acentos\n", - " text = unicodedata.normalize(\"NFD\", text)\n", - " text = \"\".join(ch for ch in text if unicodedata.category(ch) != \"Mn\")\n", - " return text" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cfaeeada", - "metadata": {}, - "outputs": [], - "source": [ - "def get_alias_set(entity: Dict[str, Any]) -> Set[str]:\n", - " \"\"\"\n", - " Devuelve el conjunto de aliases normalizados para una entidad,\n", - " asegurándose de incluir el canonical_text como alias.\n", - " \"\"\"\n", - " aliases = set()\n", - " # aliases explícitos\n", - " for a in entity.get(\"aliases\", []):\n", - " norm = normalize_text(a)\n", - " if norm:\n", - " aliases.add(norm)\n", - " # incluir canonical_text\n", - " canonical = normalize_text(entity.get(\"canonical_text\", \"\"))\n", - " if canonical:\n", - " aliases.add(canonical)\n", - " return aliases" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "789f9be0", - "metadata": {}, - "outputs": [], - "source": [ - "def alias_jaccard(aliases_gold: Set[str], aliases_pred: Set[str]) -> float:\n", - " \"\"\"\n", - " Similaridad Jaccard entre conjuntos de aliases.\n", - " \"\"\"\n", - " if not aliases_gold and not aliases_pred:\n", - " return 1.0\n", - " union = aliases_gold | aliases_pred\n", - " if not union:\n", - " return 0.0\n", - " inter = aliases_gold & aliases_pred\n", - " return len(inter) / len(union)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b551edbc", - "metadata": {}, - "outputs": [], - "source": [ - "def greedy_matching(\n", - " sim_matrix: List[List[float]], sim_threshold: float\n", - ") -> List[Tuple[int, int, float]]:\n", - " \"\"\"\n", - " Emparejamiento greedy máximo por similitud:\n", - " - sim_matrix[i][j] = similitud entre entidad gold i y pred j\n", - " - sim_threshold: mínimo para considerar un match\n", - "\n", - " Devuelve una lista de tuplas (i_gold, j_pred, sim).\n", - " \"\"\"\n", - " matches: List[Tuple[int, int, float]] = []\n", - " num_gold = len(sim_matrix)\n", - " num_pred = len(sim_matrix[0]) if num_gold > 0 else 0\n", - "\n", - " # Generar la lista de candidatos (i, j, sim) por encima del umbral\n", - " candidates: List[Tuple[float, int, int]] = []\n", - " for i in range(num_gold):\n", - " for j in range(num_pred):\n", - " sim = sim_matrix[i][j]\n", - " if sim >= sim_threshold:\n", - " candidates.append((sim, i, j))\n", - "\n", - " # Ordenar descendente por similitud\n", - " candidates.sort(reverse=True, key=lambda x: x[0])\n", - "\n", - " used_gold = set()\n", - " used_pred = set()\n", - "\n", - " for sim, i, j in candidates:\n", - " if i in used_gold or j in used_pred:\n", - " continue\n", - " used_gold.add(i)\n", - " used_pred.add(j)\n", - " matches.append((i, j, sim))\n", - "\n", - " return matches\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b59d4083", - "metadata": {}, - "outputs": [], - "source": [ - "def compute_metrics_components(\n", - " gold_entities: List[Dict[str, Any]],\n", - " pred_entities: List[Dict[str, Any]],\n", - " sim_threshold: float = 0.3,\n", - ") -> Dict[str, float]:\n", - " \"\"\"\n", - " Calcula los componentes de la métrica:\n", - " - F1_ent: F1 de entidades (detección de entidades canónicas)\n", - " - AliasF1_macro: F1 macro promedio sobre aliases por entidad emparejada\n", - " - Acc_label: accuracy de labels en entidades emparejadas\n", - " - Acc_role: accuracy de roles en entidades emparejadas\n", - " \"\"\"\n", - "\n", - " # Caso trivial: sin entidades en ambos\n", - " if not gold_entities and not pred_entities:\n", - " return {\n", - " \"F1_ent\": 1.0,\n", - " \"AliasF1_macro\": 1.0,\n", - " \"Acc_label\": 1.0,\n", - " \"Acc_role\": 1.0,\n", - " }\n", - "\n", - " # Preprocesar aliases, labels y roles\n", - " gold_aliases = [get_alias_set(e) for e in gold_entities]\n", - " pred_aliases = [get_alias_set(e) for e in pred_entities]\n", - "\n", - " gold_labels = [e.get(\"aymurai_label\") for e in gold_entities]\n", - " pred_labels = [e.get(\"aymurai_label\") for e in pred_entities]\n", - "\n", - " gold_roles = [e.get(\"attributes\", {}).get(\"role\") for e in gold_entities]\n", - " pred_roles = [e.get(\"attributes\", {}).get(\"role\") for e in pred_entities]\n", - "\n", - " num_gold = len(gold_entities)\n", - " num_pred = len(pred_entities)\n", - "\n", - " # Matriz de similitud de aliases\n", - " sim_matrix: List[List[float]] = []\n", - " for i in range(num_gold):\n", - " row = []\n", - " for j in range(num_pred):\n", - " sim = alias_jaccard(gold_aliases[i], pred_aliases[j])\n", - " row.append(sim)\n", - " sim_matrix.append(row)\n", - "\n", - " # Matching greedy\n", - " matches = greedy_matching(sim_matrix, sim_threshold=sim_threshold)\n", - "\n", - " TP = len(matches)\n", - " FN = num_gold - TP\n", - " FP = num_pred - TP\n", - "\n", - " # Entidad-level precision/recall/F1\n", - " if TP + FP > 0:\n", - " P_ent = TP / (TP + FP)\n", - " else:\n", - " P_ent = 0.0\n", - "\n", - " if TP + FN > 0:\n", - " R_ent = TP / (TP + FN)\n", - " else:\n", - " R_ent = 0.0\n", - "\n", - " if P_ent + R_ent > 0:\n", - " F1_ent = 2 * P_ent * R_ent / (P_ent + R_ent)\n", - " else:\n", - " F1_ent = 0.0\n", - "\n", - " # Alias-level F1 macro\n", - " if TP > 0:\n", - " alias_f1_sum = 0.0\n", - " label_correct = 0\n", - " role_correct = 0\n", - "\n", - " for i, j, _sim in matches:\n", - " A_g = gold_aliases[i]\n", - " A_p = pred_aliases[j]\n", - "\n", - " inter = len(A_g & A_p)\n", - " P_alias = inter / len(A_p) if len(A_p) > 0 else 0.0\n", - " R_alias = inter / len(A_g) if len(A_g) > 0 else 0.0\n", - " if P_alias + R_alias > 0:\n", - " F1_alias = 2 * P_alias * R_alias / (P_alias + R_alias)\n", - " else:\n", - " F1_alias = 0.0\n", - "\n", - " alias_f1_sum += F1_alias\n", - "\n", - " # label\n", - " if gold_labels[i] == pred_labels[j]:\n", - " label_correct += 1\n", - "\n", - " # rol\n", - " if gold_roles[i] == pred_roles[j]:\n", - " role_correct += 1\n", - "\n", - " AliasF1_macro = alias_f1_sum / TP\n", - " Acc_label = label_correct / TP\n", - " Acc_role = role_correct / TP\n", - " else:\n", - " AliasF1_macro = 0.0\n", - " Acc_label = 0.0\n", - " Acc_role = 0.0\n", - "\n", - " return {\n", - " \"F1_ent\": F1_ent,\n", - " \"AliasF1_macro\": AliasF1_macro,\n", - " \"Acc_label\": Acc_label,\n", - " \"Acc_role\": Acc_role,\n", - " }\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "14cab697", - "metadata": {}, - "outputs": [], - "source": [ - "def evaluate_disambiguation(\n", - " gold_json: Any,\n", - " pred_json: Any,\n", - " w_ent: float = 0.4,\n", - " w_alias: float = 0.35,\n", - " w_label: float = 0.2,\n", - " w_role: float = 0.05,\n", - " sim_threshold: float = 0.3,\n", - ") -> Tuple[float, Dict[str, float]]:\n", - " \"\"\"\n", - " Función principal pedida:\n", - " - gold_json: JSON ground truth (lista de entidades o string JSON)\n", - " - pred_json: JSON predicho (lista de entidades o string JSON)\n", - " - w_ent, w_alias, w_label, w_role: pesos de cada componente\n", - " - sim_threshold: umbral mínimo de similitud de aliases para emparejar entidades\n", - "\n", - " Devuelve una tupla (Score_global, métricas_componentes) donde:\n", - " - Score_global es un escalar entre 0 y 1.\n", - " - métricas_componentes es un dict con los valores individuales de cada métrica.\n", - " \"\"\"\n", - "\n", - " # Si vienen como string, parsear JSON\n", - " if isinstance(gold_json, str):\n", - " gold_entities = json.loads(gold_json)\n", - " else:\n", - " gold_entities = gold_json\n", - "\n", - " if isinstance(pred_json, str):\n", - " pred_entities = json.loads(pred_json)\n", - " else:\n", - " pred_entities = pred_json\n", - "\n", - " metrics = compute_metrics_components(\n", - " gold_entities,\n", - " pred_entities,\n", - " sim_threshold=sim_threshold,\n", - " )\n", - "\n", - " score = (\n", - " w_ent * metrics[\"F1_ent\"]\n", - " + w_alias * metrics[\"AliasF1_macro\"]\n", - " + w_label * metrics[\"Acc_label\"]\n", - " + w_role * metrics[\"Acc_role\"]\n", - " )\n", - "\n", - " return score, metrics\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12f75b12", - "metadata": {}, - "outputs": [], - "source": [ - "# Ejemplo de JSON gold y pred\n", - "gold = [\n", - " {\n", - " \"entity_id\": \"GT_1\",\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"Juan Pérez\",\n", - " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\"],\n", - " \"attributes\": {\"role\": \"imputado\"},\n", - " \"relations\": [],\n", - " }\n", - "]\n", - "\n", - "pred = [\n", - " {\n", - " \"entity_id\": \"P_1\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Juan Pérez\",\n", - " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\"],\n", - " \"attributes\": {\"role\": \"imputado\"},\n", - " \"relations\": [],\n", - " }\n", - "]\n", - "\n", - "\n", - "score, metrics = evaluate_disambiguation(gold, pred)\n", - "print(\"Metrics vector:\", metrics)\n", - "print(\"Score global:\", score)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4d1a279b", - "metadata": {}, - "outputs": [], - "source": [ - "# Ejemplo de JSON gold y pred\n", - "gold = [\n", - " {\n", - " \"entity_id\": \"GT_1\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Juan Pérez\",\n", - " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\"],\n", - " \"attributes\": {\"role\": \"imputado\"},\n", - " \"relations\": [],\n", - " },\n", - " {\n", - " \"entity_id\": \"GT_2\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"María Gómez\",\n", - " \"aliases\": [\"María\", \"Sra. Gómez\"],\n", - " \"attributes\": {\"role\": \"imputado\"},\n", - " \"relations\": [],\n", - " },\n", - "]\n", - "\n", - "pred = [\n", - " {\n", - " \"entity_id\": \"P_1\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Juan Pérez\",\n", - " \"aliases\": [\"Juan Pérez\", \"Pérez, Juan\", \"Pérez Juan\"],\n", - " \"attributes\": {\"role\": \"imputado\"},\n", - " \"relations\": [],\n", - " },\n", - " {\n", - " \"entity_id\": \"GT_2\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"María Gómez\",\n", - " \"aliases\": [\"María\", \"Sra. Gómez\"],\n", - " \"attributes\": {\"role\": \"imputado\"},\n", - " \"relations\": [],\n", - " },\n", - "]\n", - "\n", - "score, metrics = evaluate_disambiguation(gold, pred)\n", - "print(\"Metrics vector:\", metrics)\n", - "print(\"Score global:\", score)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb0c3ddf", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "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.10.19" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb b/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb deleted file mode 100644 index e907c3fc..00000000 --- a/notebooks/experiments/entity-disambiguation/04-entity-disambiguation-from-pre-clustered-validations.ipynb +++ /dev/null @@ -1,899 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c8fc128c", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext rich\n", - "\n", - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8eeefe8b", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import mimetypes\n", - "import os\n", - "import re\n", - "import sqlite3\n", - "import time\n", - "import unicodedata\n", - "from collections import Counter\n", - "from operator import itemgetter\n", - "from pathlib import Path\n", - "from typing import Iterable\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import requests\n", - "from more_itertools import flatten, unique_everseen\n", - "from rapidfuzz import fuzz, process\n", - "\n", - "from aymurai.llm_providers import OllamaLLMProvider\n", - "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", - "from aymurai.utils.json_data import get_pretty, load_json, save_json" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e0ec450", - "metadata": {}, - "outputs": [], - "source": [ - "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8999\")\n", - "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", - "DATA_ROOT = Path(\n", - " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/disambiguation-eval\")\n", - ")\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", - "\n", - "print(f\"Target endpoint: {ENDPOINT}\")\n", - "print(f\"Data root: {DATA_ROOT.resolve()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b017330", - "metadata": {}, - "outputs": [], - "source": [ - "def call_extraction_api(\n", - " session: requests.Session, file_path: Path\n", - ") -> dict[str, object]:\n", - " \"\"\"\n", - " Calls the document extraction API with the given file.\n", - "\n", - " Args:\n", - " session (requests.Session): The HTTP session to use for the request.\n", - " file_path (Path): The path to the file to be extracted.\n", - " Returns:\n", - " dict[str, object]: The response payload containing status and details.\n", - " \"\"\"\n", - " payload: dict[str, object] = {\n", - " \"path\": str(file_path),\n", - " \"status\": \"failure\",\n", - " \"status_code\": None,\n", - " \"elapsed_s\": None,\n", - " \"detail\": None,\n", - " }\n", - "\n", - " if not file_path.exists():\n", - " payload[\"detail\"] = \"File does not exist\"\n", - " return payload\n", - "\n", - " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", - " files = {\n", - " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", - " }\n", - "\n", - " try:\n", - " start = time.perf_counter()\n", - " response = session.post(\n", - " ENDPOINT,\n", - " files=files,\n", - " timeout=REQUEST_TIMEOUT,\n", - " )\n", - " elapsed = time.perf_counter() - start\n", - " except requests.RequestException as exc:\n", - " payload[\"detail\"] = f\"Request failed: {exc}\"\n", - " return payload\n", - " finally:\n", - " files[\"file\"][1].close()\n", - "\n", - " payload[\"status_code\"] = response.status_code\n", - " payload[\"elapsed_s\"] = elapsed\n", - "\n", - " try:\n", - " response_body = response.json()\n", - " except ValueError:\n", - " response_body = {\"raw\": response.text[:500]}\n", - "\n", - " if response.ok:\n", - " payload[\"status\"] = \"success\"\n", - " payload[\"detail\"] = {\n", - " \"document_id\": response_body.get(\"document_id\"),\n", - " \"document\": response_body.get(\"document\", []),\n", - " }\n", - " else:\n", - " payload[\"detail\"] = response_body\n", - "\n", - " return payload" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef352767", - "metadata": {}, - "outputs": [], - "source": [ - "if not DATA_ROOT.exists():\n", - " raise FileNotFoundError(\n", - " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", - " )\n", - "\n", - "\n", - "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "\n", - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "print(f\"Discovered {len(documents)} documents.\")\n", - "documents" - ] - }, - { - "cell_type": "markdown", - "id": "5c1e471d", - "metadata": {}, - "source": [ - "## Fetch data from database" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "281524f4", - "metadata": {}, - "outputs": [], - "source": [ - "# Anonymization document\n", - "conn = sqlite3.connect(\"/workspace/sqlite/database.db\")\n", - "anonymization_document = pd.read_sql_query(\"SELECT * FROM anonymization_document\", conn)\n", - "conn.close()\n", - "\n", - "anonymization_document" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6db4fc5", - "metadata": {}, - "outputs": [], - "source": [ - "# Anonymization document paragraphs\n", - "conn = sqlite3.connect(\"/workspace/sqlite/database.db\")\n", - "anonymization_document_paragraph = pd.read_sql_query(\n", - " \"SELECT * FROM anonymization_document_paragraph\", conn\n", - ")\n", - "conn.close()\n", - "\n", - "anonymization_document_paragraph" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ee8bf74e", - "metadata": {}, - "outputs": [], - "source": [ - "# Anonymization paragraph\n", - "conn = sqlite3.connect(\"/workspace/sqlite/database.db\")\n", - "anonymization_paragraph = pd.read_sql_query(\n", - " \"SELECT * FROM anonymization_paragraph\", conn\n", - ")\n", - "conn.close()\n", - "\n", - "anonymization_paragraph" - ] - }, - { - "cell_type": "markdown", - "id": "ffd8f27e", - "metadata": {}, - "source": [ - "## Get particular document" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25086b1a", - "metadata": {}, - "outputs": [], - "source": [ - "doc_filename = \"\" # Replace with actual document filename\n", - "\n", - "# Get document ID\n", - "document_id = anonymization_document.loc[\n", - " anonymization_document[\"name\"].str.startswith(doc_filename), \"id\"\n", - "].values[0]\n", - "document_id" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "caa4628c", - "metadata": {}, - "outputs": [], - "source": [ - "# Get paragraph IDs for the document\n", - "paragraph_ids = anonymization_document_paragraph.loc[\n", - " anonymization_document_paragraph[\"document_id\"] == document_id, \"paragraph_id\"\n", - "].tolist()\n", - "\n", - "paragraph_ids" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18307aab", - "metadata": {}, - "outputs": [], - "source": [ - "# Get paragraphs\n", - "doc_paragraphs = anonymization_paragraph.loc[\n", - " anonymization_paragraph[\"id\"].isin(paragraph_ids)\n", - "]\n", - "doc_paragraphs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c7b4ffe", - "metadata": {}, - "outputs": [], - "source": [ - "# Get validations\n", - "validations = doc_paragraphs.loc[:, \"validation\"].map(eval).tolist()\n", - "validations = list(flatten(validations))\n", - "validations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5a262978", - "metadata": {}, - "outputs": [], - "source": [ - "# Parse validations to extract labels and alt texts\n", - "validations = [\n", - " {\n", - " \"aymurai_label\": validation[\"attrs\"][\"aymurai_label\"],\n", - " \"text\": validation[\"attrs\"].get(\"aymurai_alt_text\") or validation[\"text\"],\n", - " }\n", - " for validation in validations\n", - "]\n", - "\n", - "validations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1de8c68f", - "metadata": {}, - "outputs": [], - "source": [ - "# Remove duplicates and sort\n", - "validations = list(unique_everseen(validations))\n", - "validations = sorted(\n", - " validations,\n", - " key=itemgetter(\"aymurai_label\", \"text\"),\n", - ")\n", - "validations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3fb25d74", - "metadata": {}, - "outputs": [], - "source": [ - "# Fix 'TEL' labels\n", - "for validation in validations:\n", - " if validation[\"aymurai_label\"] == \"TEL\":\n", - " validation[\"aymurai_label\"] = \"TELEFONO\"\n", - "\n", - "validations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea1d64c9", - "metadata": {}, - "outputs": [], - "source": [ - "# Map 'TEXTO_ANONIMIZAR' to 'NOMBRE_ARCHIVO' when applicable\n", - "for validation in validations:\n", - " extension = os.path.splitext(validation[\"text\"])[1].lower()\n", - " if validation[\"aymurai_label\"] == \"TEXTO_ANONIMIZAR\":\n", - " if extension.endswith(\n", - " (\n", - " \"jpg\",\n", - " \"jpeg\",\n", - " \"png\",\n", - " \"pdf\",\n", - " \"docx\",\n", - " \"odt\",\n", - " \"mp4\",\n", - " \"enc\",\n", - " )\n", - " ):\n", - " validation[\"aymurai_label\"] = \"NOMBRE_ARCHIVO\"\n", - "\n", - "validations" - ] - }, - { - "cell_type": "markdown", - "id": "68a55889", - "metadata": {}, - "source": [ - "## Cluster entities based on textual" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7fa3f5b", - "metadata": {}, - "outputs": [], - "source": [ - "def normalize(s: str) -> str:\n", - " \"\"\"\n", - " Normalize string for clustering.\n", - "\n", - " Args:\n", - " s (str): input string\n", - "\n", - " Returns:\n", - " str: normalized string\n", - " \"\"\"\n", - " # strip accents, lowercase, collapse spaces\n", - " s = \"\".join(\n", - " c for c in unicodedata.normalize(\"NFD\", s) if unicodedata.category(c) != \"Mn\"\n", - " )\n", - " s = \" \".join(s.lower().split())\n", - " return s\n", - "\n", - "\n", - "def cluster_with_cdist(\n", - " entities: list[str], threshold: int = 90, scorer: callable = fuzz.token_set_ratio\n", - "):\n", - " \"\"\"\n", - " Cluster entities based on similarity using a distance matrix.\n", - "\n", - " Args:\n", - " entities (list[str]): list of entity strings\n", - " threshold (int): similarity threshold for clustering\n", - " scorer (callable): similarity scoring function\n", - "\n", - " Returns:\n", - " list[list[tuple[str, str]]]: clusters of (original, normalized) entity tuples\n", - " \"\"\"\n", - " # matrix of similarities on normalized strings\n", - " # normalize however you like; here just lower + strip\n", - " normed = [\" \".join(e.lower().split()) for e in entities]\n", - " sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold)\n", - " sim = np.array(sim)\n", - "\n", - " # union-find\n", - " parent = list(range(len(normed)))\n", - "\n", - " def find(i):\n", - " while parent[i] != i:\n", - " parent[i] = parent[parent[i]]\n", - " i = parent[i]\n", - " return i\n", - "\n", - " def union(i, j):\n", - " ri, rj = find(i), find(j)\n", - " if ri != rj:\n", - " parent[rj] = ri\n", - "\n", - " # link pairs above threshold (upper triangle only)\n", - " n = len(normed)\n", - " for i in range(n):\n", - " for j in range(i + 1, n):\n", - " if sim[i, j] >= threshold:\n", - " union(i, j)\n", - "\n", - " # collect clusters\n", - " clusters = {}\n", - " for idx in range(n):\n", - " root = find(idx)\n", - " clusters.setdefault(root, []).append((entities[idx], normed[idx]))\n", - " return list(clusters.values())\n", - "\n", - "\n", - "def pick_canonical(cluster: list[tuple[str, str]]) -> str:\n", - " \"\"\"\n", - " Pick canonical text from a cluster.\n", - "\n", - " Args:\n", - " cluster (list[tuple[str, str]]): cluster of (original, normalized) entity tuples\n", - "\n", - " Returns:\n", - " str: chosen canonical text\n", - " \"\"\"\n", - " # prefer longest original; fall back to first\n", - " return max(cluster, key=lambda x: len(x[0]))[0]\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "82d57afd", - "metadata": {}, - "outputs": [], - "source": [ - "# Cluster entities\n", - "clusters = cluster_with_cdist(\n", - " [f\"{v['aymurai_label']}:{v['text']}\" for v in validations],\n", - " threshold=95,\n", - ")\n", - "\n", - "for cluster in clusters:\n", - " canonical = pick_canonical(cluster)\n", - " originals = [c[0] for c in cluster]\n", - " print(f\"Canonical: {canonical} -> {originals}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d542a89", - "metadata": {}, - "outputs": [], - "source": [ - "def parse_item(item: tuple[str, ...]) -> tuple[str, str, str]:\n", - " \"\"\"\n", - " Parse an item into (label, orig, norm).\n", - "\n", - " Accepts:\n", - " - (orig, norm, label)\n", - " - (labelled_orig, labelled_norm) with prefix 'LABEL:'\n", - " Args:\n", - " item (tuple[str, ...]): input item\n", - "\n", - " Returns:\n", - " tuple[str, str, str]: (label, orig, norm)\n", - " \"\"\"\n", - " if len(item) == 3:\n", - " orig, norm, label = item\n", - " return label, orig, norm\n", - "\n", - " # len == 2: assume \"LABEL:text\"\n", - " labelled_orig, labelled_norm = item\n", - " label, orig = labelled_orig.split(\":\", 1)\n", - " _, norm = labelled_norm.split(\":\", 1)\n", - " return label, orig, norm\n", - "\n", - "\n", - "def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str:\n", - " \"\"\"\n", - " Pick the most common label from parsed items.\n", - "\n", - " Args:\n", - " parsed_items (list[tuple[str, str, str]]): parsed items\n", - "\n", - " Returns:\n", - " str: chosen label\n", - " \"\"\"\n", - " labels = [lbl for lbl, _, _ in parsed_items]\n", - " # majority vote; fallback to first\n", - " return Counter(labels).most_common(1)[0][0]\n", - "\n", - "\n", - "def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str:\n", - " \"\"\"\n", - " Choose the longest original surface form; tweak as needed.\n", - "\n", - " Args:\n", - " parsed_items (list[tuple[str, str, str]]): parsed items\n", - "\n", - " Returns:\n", - " str: chosen canonical text\n", - " \"\"\"\n", - " return max(parsed_items, key=lambda x: len(x[1]))[1]\n", - "\n", - "\n", - "def clusters_to_canonical_entities(\n", - " clusters: list[list[tuple[str, str]]],\n", - ") -> list[CanonicalEntity]:\n", - " \"\"\"\n", - " Convert clusters to CanonicalEntity objects.\n", - "\n", - " Args:\n", - " clusters (list[list[tuple[str, str]]]): clusters of (original, normalized) entity tuples\n", - "\n", - " Returns:\n", - " list[CanonicalEntity]: list of CanonicalEntity objects\n", - " \"\"\"\n", - " canonical_entities = []\n", - "\n", - " for cluster in clusters:\n", - " parsed = [parse_item(item) for item in cluster] # [(label, orig, norm), ...]\n", - " label = pick_cluster_label(parsed)\n", - " canonical_text = pick_canonical_text(parsed)\n", - " aliases = sorted({orig for _, orig, _ in parsed})\n", - " ce = CanonicalEntity(\n", - " aymurai_label=label,\n", - " canonical_text=canonical_text,\n", - " aliases=aliases,\n", - " attributes={},\n", - " relations=[],\n", - " )\n", - " canonical_entities.append(ce)\n", - "\n", - " return canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "646a5e41", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = clusters_to_canonical_entities(clusters)\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "edbf9486", - "metadata": {}, - "outputs": [], - "source": [ - "# Sort by label and canonical text\n", - "canonical_entities = sorted(\n", - " canonical_entities,\n", - " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", - ")\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e063eb07", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare target filename\n", - "target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_filename))[0]\n", - ")\n", - "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)\n", - "\n", - "# Save canonical entities to JSON for reviewing\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"/resources/data/restricted/disambiguation-eval/canonical-entities/REVIEWING/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81be5a59", - "metadata": {}, - "outputs": [], - "source": [ - "# Execute this cell to load reviewed canonical entities\n", - "reviewed_canonical_entities = load_json(\n", - " \"/resources/data/restricted/disambiguation-eval/canonical-entities/REVIEWING/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", - "]\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "893a6e06", - "metadata": {}, - "outputs": [], - "source": [ - "# Persist reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"/resources/data/restricted/disambiguation-eval/canonical-entities/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "37eb822d", - "metadata": {}, - "source": [ - "## CanonicalEntity extraction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5eed876e", - "metadata": {}, - "outputs": [], - "source": [ - "# Available models\n", - "model = \"phi4:14b\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73400ead", - "metadata": {}, - "outputs": [], - "source": [ - "# Sanity check\n", - "provider = OllamaLLMProvider(model=model)\n", - "provider.generate(\"hola, ¿cómo estás?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b8ac734", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt = \"\"\"\n", - "Eres un asistente especializado en anonimización de sentencias judiciales.\n", - "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", - "Una **entidad canónica** es la representación única de una entidad real.\n", - "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", - "\n", - "# Reglas\n", - "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", - "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", - "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", - "- Cada entidad canónica debe incluir:\n", - " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", - " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", - " - `aliases` (todas las menciones textuales relevantes).\n", - " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", - "\n", - "# Notas\n", - "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", - "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - "\n", - "# Ejemplo\n", - "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", - "A: ```json\n", - "[\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Laura Beatriz Gómez\",\n", - " \"aliases\": [\"Laura Beatriz Gómez\"],\n", - " \"attributes\": {\"role\": \"Denunciante\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"42987654\",\n", - " \"aliases\": [\"42.987.654\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DIRECCION\",\n", - " \"canonical_text\": \"calle Falsa 123\",\n", - " \"aliases\": [\"calle Falsa 123\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", - " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", - " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", - " \"attributes\": {\"role\": \"Denunciado\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"27654321\",\n", - " \"aliases\": [\"27.654.321\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"calle Real 789\",\n", - " \"aliases\": [\"calle Real 789\"]\n", - " }\n", - "]\n", - "```\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "50d9cd2d", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template = \"\"\"\n", - "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", - "\n", - "# Documento\n", - "{document_text}\n", - "\n", - "# Menciones de entidades canónicas agrupadas\n", - "{canonical_entities}\n", - "\n", - "# Instrucciones\n", - "1. Evalúa cada entidad canónica listada, poniendo especial atención en las aliases.\n", - "2. Si alguna alias no corresponde a esa entidad, elimínala de la lista de aliases y créala como una nueva entidad canónica.\n", - "3. Las diferencias textuales correctas en los aliases pueden ser variantes ortográficas, abreviaciones, iniciales o errores tipográficos.\n", - "4. Diferencias sutiles que permiten desambiguar entidades distintas (por ejemplo, nombres similares de personas diferentes, fechas próximas pero distintas, números de documentos, códigos de identificación o nombres de archivos con pequeñas variaciones, etc.).\n", - "5. Normaliza canonical_text; mantén los aliases tal como aparecen.\n", - "6. Identifica roles u otros atributos relevantes en el campo attributes (por ejemplo, {{\"role\": \"Denunciante\"}}).\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81f47270", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "session = requests.Session()\n", - "doc_path = next((doc for doc in documents if doc.name == doc_filename), None)\n", - "document = call_extraction_api(session, Path(doc_path))\n", - "document = document.get(\"detail\", {}).get(\"document\")\n", - "\n", - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f6143dc5", - "metadata": {}, - "outputs": [], - "source": [ - "# Remove empty 'entity_id', 'relations' and 'attributes' fields\n", - "canonical_entities = [\n", - " {\n", - " k: v\n", - " for k, v in ce.items()\n", - " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", - " }\n", - " for ce in canonical_entities\n", - "]\n", - "\n", - "# Prepare user prompt\n", - "user_prompt = user_prompt_template.format(\n", - " document_text=\"\\n\".join(document).strip(),\n", - " canonical_entities=get_pretty(canonical_entities),\n", - ")\n", - "\n", - "print(user_prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd772da7", - "metadata": {}, - "outputs": [], - "source": [ - "# Get canonical entities from the model\n", - "provider = OllamaLLMProvider(model=model)\n", - "response = provider.generate(\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options={\"temperature\": 0, \"num_ctx\": 16_384},\n", - " format=CanonicalEntities.model_json_schema(),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "adb137aa", - "metadata": {}, - "outputs": [], - "source": [ - "response" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "687c02b5", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai (3.10.19)", - "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.10.19" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb b/notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb deleted file mode 100644 index f34b64d0..00000000 --- a/notebooks/experiments/entity-disambiguation/05-entity-disambiguation-pre-clustered-manual-validation.ipynb +++ /dev/null @@ -1,2447 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c8fc128c", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext rich\n", - "\n", - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8eeefe8b", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import json\n", - "import mimetypes\n", - "import os\n", - "import re\n", - "import time\n", - "import unicodedata\n", - "from collections import Counter\n", - "from operator import itemgetter\n", - "from pathlib import Path\n", - "from typing import Iterable\n", - "\n", - "import requests\n", - "from tqdm import tqdm\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import requests\n", - "from more_itertools import flatten, unique_everseen\n", - "from rapidfuzz import fuzz, process\n", - "\n", - "from aymurai.llm_providers import OllamaLLMProvider\n", - "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", - "from aymurai.utils.json_data import get_pretty, save_json, load_json" - ] - }, - { - "cell_type": "markdown", - "id": "84d11bba", - "metadata": {}, - "source": [ - "## /document-extract endpoint output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e0ec450", - "metadata": {}, - "outputs": [], - "source": [ - "API_URL = os.getenv(\"DOCUMENT_API_BASE_URL\", \"http://127.0.0.1:8000\")\n", - "ENDPOINT = f\"{API_URL}/misc/document-extract\"\n", - "DATA_ROOT = Path(\n", - " os.getenv(\"DOCUMENT_DATA_ROOT\", \"../../../resources/data/restricted/disambiguation-eval/files\")\n", - ")\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", - "\n", - "print(f\"Target endpoint: {ENDPOINT}\")\n", - "print(f\"Data root: {DATA_ROOT.resolve()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef352767", - "metadata": {}, - "outputs": [], - "source": [ - "if not DATA_ROOT.exists():\n", - " raise FileNotFoundError(\n", - " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", - " )\n", - "\n", - "\n", - "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "\n", - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "print(f\"Discovered {len(documents)} documents.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "21fd6e36", - "metadata": {}, - "outputs": [], - "source": [ - "def call_extraction_api(\n", - " session: requests.Session, file_path: Path\n", - ") -> dict[str, object]:\n", - " payload: dict[str, object] = {\n", - " \"path\": str(file_path),\n", - " \"status\": \"failure\",\n", - " \"status_code\": None,\n", - " \"elapsed_s\": None,\n", - " \"detail\": None,\n", - " }\n", - "\n", - " if not file_path.exists():\n", - " payload[\"detail\"] = \"File does not exist\"\n", - " return payload\n", - "\n", - " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", - " files = {\n", - " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", - " }\n", - "\n", - " try:\n", - " start = time.perf_counter()\n", - " response = session.post(\n", - " ENDPOINT,\n", - " files=files,\n", - " timeout=REQUEST_TIMEOUT_S,\n", - " )\n", - " elapsed = time.perf_counter() - start\n", - " except requests.RequestException as exc:\n", - " payload[\"detail\"] = f\"Request failed: {exc}\"\n", - " return payload\n", - " finally:\n", - " files[\"file\"][1].close()\n", - "\n", - " payload[\"status_code\"] = response.status_code\n", - " payload[\"elapsed_s\"] = elapsed\n", - "\n", - " try:\n", - " response_body = response.json()\n", - " except ValueError:\n", - " response_body = {\"raw\": response.text[:500]}\n", - "\n", - " if response.ok:\n", - " payload[\"status\"] = \"success\"\n", - " payload[\"detail\"] = {\n", - " \"document_id\": response_body.get(\"document_id\"),\n", - " \"document\": response_body.get(\"document\", []),\n", - " }\n", - " else:\n", - " payload[\"detail\"] = response_body\n", - "\n", - " return payload" - ] - }, - { - "cell_type": "markdown", - "id": "9254ef68", - "metadata": {}, - "source": [ - "## Inference" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a308cf90", - "metadata": {}, - "outputs": [], - "source": [ - "# Function to make inference using the API\n", - "def get_predictions(sample: str) -> dict:\n", - " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample})\n", - " response.raise_for_status()\n", - " return response.json()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7ef6a0fa", - "metadata": {}, - "outputs": [], - "source": [ - "from itertools import chain\n", - "from operator import itemgetter\n", - "from typing import Any\n", - "\n", - "from more_itertools import unique_everseen\n", - "\n", - "\n", - "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", - " \"\"\"\n", - " Parse prediction labels to extract unique aymurai_label and aymurai_alt_text pairs.\n", - "\n", - " Args:\n", - " predictions (list[dict[str, Any]]): A list of prediction dictionaries.\n", - "\n", - " Returns:\n", - " list[dict[str, str]]: A list of dictionaries containing unique aymurai_label and aymurai_alt_text pairs.\n", - " \"\"\"\n", - " attrs_stream = (\n", - " label.get(\"attrs\") or {}\n", - " for label in chain.from_iterable(pred.get(\"labels\", ()) for pred in predictions)\n", - " )\n", - "\n", - " unique_pairs = unique_everseen(\n", - " (\n", - " attrs.get(\"aymurai_label\"),\n", - " attrs.get(\"aymurai_alt_text\"),\n", - " )\n", - " for attrs in attrs_stream\n", - " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", - " )\n", - "\n", - " return sorted(\n", - " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", - " key=itemgetter(\"aymurai_label\", \"text\"),\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "52d6b14d", - "metadata": {}, - "source": [ - "# 8241_90_17_10_2025_128 CAUSA 14663_2020-2" - ] - }, - { - "cell_type": "markdown", - "id": "7e113ea4", - "metadata": {}, - "source": [ - "In this case, I took the .json file that Juli passed me and made a first manual correction of the clustering he did in the [notebook 04 pipeline](04-entity-disambiguation-from-pre-clustered-validations.ipynb).\n", - "\n", - "For this .json, a function needs to be created to ensure that, instead of grouping the files into a single canonical entity, it divides each one into a different canonical entity of the type ***NOMBRE_ARCHIVO***." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa6bbadf", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare target filename\n", - "target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(documents[1]))[0]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "934be599", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare json to review\n", - "json_path = \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - "target_path = json_path + target_filename + \"-canonical-entities.json\"\n", - "canonical_entities_to_review = load_json(target_path)\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities_to_review = [\n", - " CanonicalEntity.model_validate(entity) for entity in canonical_entities_to_review\n", - "]\n", - "canonical_entities_to_review = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities_to_review\n", - "]\n", - "\n", - "canonical_entities_to_review" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b94f9ec8", - "metadata": {}, - "outputs": [], - "source": [ - "from copy import deepcopy\n", - "\n", - "def split_aliases_by_label(items: list[CanonicalEntity], target_label: str) -> list[CanonicalEntity]:\n", - " \"\"\"\n", - " Splits aliases into new CanonicalEntities if the item matches target_label.\n", - " Removes non-matching aliases from the original entity.\n", - " \"\"\"\n", - " processed_items = []\n", - "\n", - " for item in items:\n", - " # pass through items that don't match the target label\n", - " if item['aymurai_label'] != target_label:\n", - " processed_items.append(item)\n", - " continue\n", - "\n", - " # if it matches, we process the aliases\n", - " kept_aliases = []\n", - " new_entities = []\n", - "\n", - " for alias in item['aliases']:\n", - " if alias == item['canonical_text']:\n", - " # Keep this alias in the original item\n", - " kept_aliases.append(alias)\n", - " else:\n", - " # Create a NEW entity for this alias\n", - " new_ce = CanonicalEntity(\n", - " aymurai_label=target_label,\n", - " canonical_text=alias, # The alias becomes the new canonical text\n", - " aliases=[alias], # New entity starts with no aliases\n", - " attributes=deepcopy(item['attributes']),\n", - " relations=deepcopy(item['relations'])\n", - " )\n", - " new_entities.append(new_ce)\n", - "\n", - " # update the original item's aliases\n", - " item['aliases'] = kept_aliases\n", - " \n", - " # add the original item to the result\n", - " processed_items.append(item)\n", - " \n", - " # add the newly created entities to the result\n", - " processed_items.extend(new_entities)\n", - "\n", - " return processed_items" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d86ee5bb", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities_to_review = split_aliases_by_label(canonical_entities_to_review, target_label='NOMBRE_ARCHIVO')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b21bc5f4", - "metadata": {}, - "outputs": [], - "source": [ - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in canonical_entities_to_review\n", - "]\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b9218a8", - "metadata": {}, - "outputs": [], - "source": [ - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7e362c6", - "metadata": {}, - "outputs": [], - "source": [ - "# Execute this cell to load reviewed canonical entities\n", - "reviewed_canonical_entities = load_json(\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", - "]\n", - "\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d138d674", - "metadata": {}, - "outputs": [], - "source": [ - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "973600c7", - "metadata": {}, - "source": [ - "## CanonicalEntity extraction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "16a8520a", - "metadata": {}, - "outputs": [], - "source": [ - "# Available models\n", - "model = \"phi4:14b\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "63c6d798", - "metadata": {}, - "outputs": [], - "source": [ - "# Sanity check\n", - "provider = OllamaLLMProvider(model=model)\n", - "provider.generate(\"hola, ¿cómo estás?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e665c5a5", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " stream_prompt = \"hola, ¿cómo estás?\"\n", - " pieces = []\n", - " for event in provider.stream(stream_prompt):\n", - " print(event.text, end=\"\", flush=True)\n", - " pieces.append(event.text)\n", - " print()\n", - " full_text = \"\".join(pieces)\n", - "\n", - "except Exception as exc:\n", - " print(\"La transmisión no está disponible:\", exc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "80706810", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt = \"\"\"\n", - "Eres un asistente especializado en anonimización de sentencias judiciales.\n", - "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", - "Una **entidad canónica** es la representación única de una entidad real.\n", - "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", - "\n", - "# Reglas\n", - "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", - "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", - "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", - "- Cada entidad canónica debe incluir:\n", - " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", - " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", - " - `aliases` (todas las menciones textuales relevantes).\n", - " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", - "\n", - "# Notas\n", - "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", - "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - "\n", - "# Ejemplo\n", - "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", - "A: ```json\n", - "[\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Laura Beatriz Gómez\",\n", - " \"aliases\": [\"Laura Beatriz Gómez\"],\n", - " \"attributes\": {\"role\": \"Denunciante\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"42987654\",\n", - " \"aliases\": [\"42.987.654\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DIRECCION\",\n", - " \"canonical_text\": \"calle Falsa 123\",\n", - " \"aliases\": [\"calle Falsa 123\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", - " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", - " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", - " \"attributes\": {\"role\": \"Denunciado\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"27654321\",\n", - " \"aliases\": [\"27.654.321\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"calle Real 789\",\n", - " \"aliases\": [\"calle Real 789\"]\n", - " }\n", - "]\n", - "```\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "055fa7a6", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template = \"\"\"\n", - "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", - "\n", - "# Documento\n", - "{document_text}\n", - "\n", - "# Menciones de entidades canónicas agrupadas\n", - "{canonical_entities}\n", - "\n", - "# Instrucciones\n", - "1. Evalúa cada entidad canónica listada, poniendo especial atención en las aliases.\n", - "2. Si alguna alias no corresponde a esa entidad, elimínala de la lista de aliases y créala como una nueva entidad canónica.\n", - "3. Las diferencias textuales correctas en los aliases pueden ser variantes ortográficas, abreviaciones, iniciales o errores tipográficos.\n", - "4. Diferencias sutiles que permiten desambiguar entidades distintas (por ejemplo, nombres similares de personas diferentes, fechas próximas pero distintas, números de documentos, códigos de identificación o nombres de archivos con pequeñas variaciones, etc.).\n", - "5. Normaliza canonical_text; mantén los aliases tal como aparecen.\n", - "6. Identifica roles u otros atributos relevantes en el campo attributes (por ejemplo, {{\"role\": \"Denunciante\"}}).\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bc94b25", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "session = requests.Session()\n", - "document = call_extraction_api(session, Path(documents[1]))\n", - "document = document.get(\"detail\", {}).get(\"document\")\n", - "\n", - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d53eae0", - "metadata": {}, - "outputs": [], - "source": [ - "# Get paragraphs with at least one NER predicted label\n", - "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - "pred_paragraphs = [paragraph['document'] for paragraph in ner_predictions if paragraph['labels']]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d2b8a84d", - "metadata": {}, - "outputs": [], - "source": [ - "# Remove empty 'entity_id', 'relations' and 'attributes' fields\n", - "canonical_entities = [\n", - " {\n", - " k: v\n", - " for k, v in ce.items()\n", - " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", - " }\n", - " for ce in canonical_entities\n", - "]\n", - "\n", - "# Prepare user prompt\n", - "user_prompt = user_prompt_template.format(\n", - " document_text=\"\\n\".join(pred_paragraphs).strip(),\n", - " canonical_entities=get_pretty(canonical_entities),\n", - ")\n", - "\n", - "print(user_prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "04e638df", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " stream_prompt = messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ]\n", - " pieces = []\n", - " for event in provider.stream(\n", - " messages=stream_prompt,\n", - " options={\"temperature\": 0, \"num_ctx\": 9_500},\n", - " format=CanonicalEntities.model_json_schema(),\n", - " ):\n", - " print(event.text, end=\"\", flush=True)\n", - " pieces.append(event.text)\n", - " print()\n", - " full_text = \"\".join(pieces)\n", - "\n", - "except Exception as exc:\n", - " print(\"La transmisión no está disponible:\", exc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1377bcfa", - "metadata": {}, - "outputs": [], - "source": [ - "# Get canonical entities from the model\n", - "\n", - "provider = OllamaLLMProvider(model=model)\n", - "response = provider.generate(\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options={\"temperature\": 0, \"num_ctx\": 16_384},\n", - " format=CanonicalEntities.model_json_schema(),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2868141f", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = [\n", - " output for output in json.loads(response.text)[\"canonical_entities\"]\n", - " ]\n", - "\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(canonical_entity)\n", - " for canonical_entity in canonical_entities\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0337fe0c", - "metadata": {}, - "outputs": [], - "source": [ - "# Save LLM-predicted canonical entities\n", - "os.makedirs(\"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\", exist_ok=True) \n", - "\n", - "target_path = Path(\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "save_json(\n", - " [\n", - " canonical_entity.model_dump()\n", - " | {\"entity_id\": canonical_entity.entity_id.hex}\n", - " for canonical_entity in canonical_entities\n", - " ],\n", - " str(target_path),\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "1ff322aa", - "metadata": {}, - "source": [ - "# 01 - NN x 5C" - ] - }, - { - "cell_type": "markdown", - "id": "924a5fc2", - "metadata": {}, - "source": [ - "# Pipeline NER - Clusterization - Validation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2197430d", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "session = requests.Session()\n", - "document = call_extraction_api(session, Path(documents[0]))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0e8b1e99", - "metadata": {}, - "outputs": [], - "source": [ - "document = document.get(\"detail\", {}).get(\"document\")\n", - "\n", - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")\n", - "\n", - "# Get NER predictions\n", - "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", - "ner_output_json = get_pretty(parsed_ner_labels)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "74407f30", - "metadata": {}, - "outputs": [], - "source": [ - "def normalize(s: str) -> str:\n", - " \"\"\"\n", - " Normalize string for clustering.\n", - "\n", - " Args:\n", - " s (str): input string\n", - "\n", - " Returns:\n", - " str: normalized string\n", - " \"\"\"\n", - " # strip accents, lowercase, collapse spaces\n", - " s = \"\".join(\n", - " c for c in unicodedata.normalize(\"NFD\", s) if unicodedata.category(c) != \"Mn\"\n", - " )\n", - " s = \" \".join(s.lower().split())\n", - " return s\n", - "\n", - "\n", - "def cluster_with_cdist(\n", - " entities: list[str], threshold: int = 90, scorer: callable = fuzz.token_set_ratio\n", - "):\n", - " \"\"\"\n", - " Cluster entities based on similarity using a distance matrix.\n", - "\n", - " Args:\n", - " entities (list[str]): list of entity strings\n", - " threshold (int): similarity threshold for clustering\n", - " scorer (callable): similarity scoring function\n", - "\n", - " Returns:\n", - " list[list[tuple[str, str]]]: clusters of (original, normalized) entity tuples\n", - " \"\"\"\n", - " # matrix of similarities on normalized strings\n", - " # normalize however you like; here just lower + strip\n", - " normed = [\" \".join(e.lower().split()) for e in entities]\n", - " sim = process.cdist(normed, normed, scorer=scorer, score_cutoff=threshold)\n", - " sim = np.array(sim)\n", - "\n", - " # union-find\n", - " parent = list(range(len(normed)))\n", - "\n", - " def find(i):\n", - " while parent[i] != i:\n", - " parent[i] = parent[parent[i]]\n", - " i = parent[i]\n", - " return i\n", - "\n", - " def union(i, j):\n", - " ri, rj = find(i), find(j)\n", - " if ri != rj:\n", - " parent[rj] = ri\n", - "\n", - " # link pairs above threshold (upper triangle only)\n", - " n = len(normed)\n", - " for i in range(n):\n", - " for j in range(i + 1, n):\n", - " if sim[i, j] >= threshold:\n", - " union(i, j)\n", - "\n", - " # collect clusters\n", - " clusters = {}\n", - " for idx in range(n):\n", - " root = find(idx)\n", - " clusters.setdefault(root, []).append((entities[idx], normed[idx]))\n", - " return list(clusters.values())\n", - "\n", - "\n", - "def pick_canonical(cluster: list[tuple[str, str]]) -> str:\n", - " \"\"\"\n", - " Pick canonical text from a cluster.\n", - "\n", - " Args:\n", - " cluster (list[tuple[str, str]]): cluster of (original, normalized) entity tuples\n", - "\n", - " Returns:\n", - " str: chosen canonical text\n", - " \"\"\"\n", - " # prefer longest original; fall back to first\n", - " return max(cluster, key=lambda x: len(x[0]))[0]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6a355dc9", - "metadata": {}, - "outputs": [], - "source": [ - "# Cluster entities\n", - "clusters = cluster_with_cdist(\n", - " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", - " threshold=95,\n", - ")\n", - "\n", - "for cluster in clusters:\n", - " canonical = pick_canonical(cluster)\n", - " originals = [c[0] for c in cluster]\n", - " print(f\"Canonical: {canonical} -> {originals}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a1f5b54c", - "metadata": {}, - "outputs": [], - "source": [ - "def parse_item(item: tuple[str, ...]) -> tuple[str, str, str]:\n", - " \"\"\"\n", - " Parse an item into (label, orig, norm).\n", - "\n", - " Accepts:\n", - " - (orig, norm, label)\n", - " - (labelled_orig, labelled_norm) with prefix 'LABEL:'\n", - " Args:\n", - " item (tuple[str, ...]): input item\n", - "\n", - " Returns:\n", - " tuple[str, str, str]: (label, orig, norm)\n", - " \"\"\"\n", - " if len(item) == 3:\n", - " orig, norm, label = item\n", - " return label, orig, norm\n", - "\n", - " # len == 2: assume \"LABEL:text\"\n", - " labelled_orig, labelled_norm = item\n", - " label, orig = labelled_orig.split(\":\", 1)\n", - " _, norm = labelled_norm.split(\":\", 1)\n", - " return label, orig, norm\n", - "\n", - "\n", - "def pick_cluster_label(parsed_items: list[tuple[str, str, str]]) -> str:\n", - " \"\"\"\n", - " Pick the most common label from parsed items.\n", - "\n", - " Args:\n", - " parsed_items (list[tuple[str, str, str]]): parsed items\n", - "\n", - " Returns:\n", - " str: chosen label\n", - " \"\"\"\n", - " labels = [lbl for lbl, _, _ in parsed_items]\n", - " # majority vote; fallback to first\n", - " return Counter(labels).most_common(1)[0][0]\n", - "\n", - "\n", - "def pick_canonical_text(parsed_items: list[tuple[str, str, str]]) -> str:\n", - " \"\"\"\n", - " Choose the longest original surface form; tweak as needed.\n", - "\n", - " Args:\n", - " parsed_items (list[tuple[str, str, str]]): parsed items\n", - "\n", - " Returns:\n", - " str: chosen canonical text\n", - " \"\"\"\n", - " return max(parsed_items, key=lambda x: len(x[1]))[1]\n", - "\n", - "\n", - "def clusters_to_canonical_entities(\n", - " clusters: list[list[tuple[str, str]]],\n", - ") -> list[CanonicalEntity]:\n", - " \"\"\"\n", - " Convert clusters to CanonicalEntity objects.\n", - "\n", - " Args:\n", - " clusters (list[list[tuple[str, str]]]): clusters of (original, normalized) entity tuples\n", - "\n", - " Returns:\n", - " list[CanonicalEntity]: list of CanonicalEntity objects\n", - " \"\"\"\n", - " canonical_entities = []\n", - "\n", - " for cluster in clusters:\n", - " parsed = [parse_item(item) for item in cluster] # [(label, orig, norm), ...]\n", - " label = pick_cluster_label(parsed)\n", - " canonical_text = pick_canonical_text(parsed)\n", - " aliases = sorted({orig for _, orig, _ in parsed})\n", - " ce = CanonicalEntity(\n", - " aymurai_label=label,\n", - " canonical_text=canonical_text,\n", - " aliases=aliases,\n", - " attributes={},\n", - " relations=[],\n", - " )\n", - " canonical_entities.append(ce)\n", - "\n", - " return canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "758cf776", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = clusters_to_canonical_entities(clusters)\n", - "\n", - "# Sort by label and canonical text\n", - "canonical_entities = sorted(\n", - " canonical_entities,\n", - " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", - ")\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6ba35eb5", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare target filename\n", - "target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(documents[0]))[0]\n", - ")\n", - "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "19f0af6f", - "metadata": {}, - "outputs": [], - "source": [ - "target_filename" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bab1a112", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# Save canonical entities to JSON in to-review folder to save the raw output\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save canonical entities to JSON in reviewing folder to make te review manually\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "8cc94d9c", - "metadata": {}, - "source": [ - "Make a pause and review the json manually for those wrong outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "17c09855", - "metadata": {}, - "outputs": [], - "source": [ - "# Execute this cell to load reviewed canonical entities\n", - "reviewed_canonical_entities = load_json(\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", - "]\n", - "\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64ac9530", - "metadata": {}, - "outputs": [], - "source": [ - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "40a2dced", - "metadata": {}, - "source": [ - "## CanonicalEntity extraction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e1e15dce", - "metadata": {}, - "outputs": [], - "source": [ - "# Available models\n", - "model = \"phi4:14b\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "edba631d", - "metadata": {}, - "outputs": [], - "source": [ - "# Sanity check\n", - "provider = OllamaLLMProvider(model=model)\n", - "provider.generate(\"hola, ¿cómo estás?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cdb2d734", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt = \"\"\"\n", - "Eres un asistente especializado en anonimización de sentencias judiciales.\n", - "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", - "Una **entidad canónica** es la representación única de una entidad real.\n", - "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", - "\n", - "# Reglas\n", - "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", - "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", - "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", - "- Cada entidad canónica debe incluir:\n", - " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", - " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", - " - `aliases` (todas las menciones textuales relevantes).\n", - " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", - "\n", - "# Notas\n", - "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", - "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - "\n", - "# Ejemplo\n", - "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", - "A: ```json\n", - "[\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Laura Beatriz Gómez\",\n", - " \"aliases\": [\"Laura Beatriz Gómez\"],\n", - " \"attributes\": {\"role\": \"Denunciante\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"42987654\",\n", - " \"aliases\": [\"42.987.654\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DIRECCION\",\n", - " \"canonical_text\": \"calle Falsa 123\",\n", - " \"aliases\": [\"calle Falsa 123\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", - " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", - " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", - " \"attributes\": {\"role\": \"Denunciado\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"27654321\",\n", - " \"aliases\": [\"27.654.321\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"calle Real 789\",\n", - " \"aliases\": [\"calle Real 789\"]\n", - " }\n", - "]\n", - "```\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bedf1a83", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template = \"\"\"\n", - "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", - "\n", - "# Documento\n", - "{document_text}\n", - "\n", - "# Menciones de entidades canónicas agrupadas\n", - "{canonical_entities}\n", - "\n", - "# Instrucciones\n", - "1. Evalúa cada entidad canónica listada, poniendo especial atención en las aliases.\n", - "2. Si alguna alias no corresponde a esa entidad, elimínala de la lista de aliases y créala como una nueva entidad canónica.\n", - "3. Las diferencias textuales correctas en los aliases pueden ser variantes ortográficas, abreviaciones, iniciales o errores tipográficos.\n", - "4. Diferencias sutiles que permiten desambiguar entidades distintas (por ejemplo, nombres similares de personas diferentes, fechas próximas pero distintas, números de documentos, códigos de identificación o nombres de archivos con pequeñas variaciones, etc.).\n", - "5. Normaliza canonical_text; mantén los aliases tal como aparecen.\n", - "6. Identifica roles u otros atributos relevantes en el campo attributes (por ejemplo, {{\"role\": \"Denunciante\"}}).\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "be7ced82", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "session = requests.Session()\n", - "document = call_extraction_api(session, Path(documents[0]))\n", - "document = document.get(\"detail\", {}).get(\"document\")\n", - "\n", - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dc9607a8", - "metadata": {}, - "outputs": [], - "source": [ - "# Get paragraphs with at least one NER predicted label\n", - "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - "pred_paragraphs = [paragraph['document'] for paragraph in ner_predictions if paragraph['labels']]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "42a89610", - "metadata": {}, - "outputs": [], - "source": [ - "# Remove empty 'entity_id', 'relations' and 'attributes' fields\n", - "canonical_entities = [\n", - " {\n", - " k: v\n", - " for k, v in ce.items()\n", - " if k not in (\"entity_id\", \"relations\", \"attributes\") or v\n", - " }\n", - " for ce in canonical_entities\n", - "]\n", - "\n", - "# Prepare user prompt\n", - "user_prompt = user_prompt_template.format(\n", - " document_text=\"\\n\".join(document).strip(),\n", - " canonical_entities=get_pretty(canonical_entities),\n", - ")\n", - "\n", - "print(user_prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6d3e2bc9", - "metadata": {}, - "outputs": [], - "source": [ - "# Get canonical entities from the model\n", - "provider = OllamaLLMProvider(model=model)\n", - "response = provider.generate(\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options={\"temperature\": 0, \"num_ctx\": 9_500},\n", - " format=CanonicalEntities.model_json_schema(),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "172ed8d4", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = [\n", - " output for output in json.loads(response.text)[\"canonical_entities\"]\n", - " ]\n", - "\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(canonical_entity)\n", - " for canonical_entity in canonical_entities\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "87be578d", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24833143", - "metadata": {}, - "outputs": [], - "source": [ - "# Save LLM-predicted canonical entities\n", - "os.makedirs(\"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\", exist_ok=True) \n", - "\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "9e0a7186", - "metadata": {}, - "source": [ - "# 1- FLORES (pdf)" - ] - }, - { - "cell_type": "markdown", - "id": "f758c28c", - "metadata": {}, - "source": [ - "# Pipeline NER - Clusterization - Validation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f59197b", - "metadata": {}, - "outputs": [], - "source": [ - "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/1- FLORES ABARCA, Francisco Alexander s 149 bis J-01-00369422-0-2022-1 Sala Feria (II) nnya vict do apela pp_response_1765835782205.json\"\n", - "\n", - "json_path = Path(json_path)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0d1e5f7b", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "document = load_json(json_path)[\"document\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb8086f8", - "metadata": {}, - "outputs": [], - "source": [ - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")\n", - "\n", - "# Get NER predictions\n", - "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", - "ner_output_json = get_pretty(parsed_ner_labels)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "afde9c03", - "metadata": {}, - "outputs": [], - "source": [ - "parsed_ner_labels" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bc66c703", - "metadata": {}, - "outputs": [], - "source": [ - "# Cluster entities\n", - "clusters = cluster_with_cdist(\n", - " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", - " threshold=95,\n", - ")\n", - "\n", - "for cluster in clusters:\n", - " canonical = pick_canonical(cluster)\n", - " originals = [c[0] for c in cluster]\n", - " print(f\"Canonical: {canonical} -> {originals}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a6d06d5b", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = clusters_to_canonical_entities(clusters)\n", - "\n", - "# Sort by label and canonical text\n", - "canonical_entities = sorted(\n", - " canonical_entities,\n", - " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", - ")\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5c62763a", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare target filename\n", - "target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", - ")\n", - "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3317ee7f", - "metadata": {}, - "outputs": [], - "source": [ - "# Save canonical entities to JSON in to-review folder to save the raw output\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save canonical entities to JSON in reviewing folder to make te review manually\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "4add5e66", - "metadata": {}, - "source": [ - "Make a pause and review the json manually for those wrong outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5b56adf8", - "metadata": {}, - "outputs": [], - "source": [ - "# Execute this cell to load reviewed canonical entities\n", - "reviewed_canonical_entities = load_json(\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", - "]\n", - "\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b30019e1", - "metadata": {}, - "outputs": [], - "source": [ - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "e5f587ef", - "metadata": {}, - "source": [ - "# 2 - FISSCHER (pdf)" - ] - }, - { - "cell_type": "markdown", - "id": "9d446c92", - "metadata": {}, - "source": [ - "# Pipeline NER - Clusterization - Validation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a45a578b", - "metadata": {}, - "outputs": [], - "source": [ - "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/2 - FISSCHER Alejandro Claudio s - 92_response_1765908855010.json\"\n", - "\n", - "json_path = Path(json_path)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "37fea663", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "document = load_json(json_path)[\"document\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ca4de17f", - "metadata": {}, - "outputs": [], - "source": [ - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")\n", - "\n", - "# Get NER predictions\n", - "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", - "ner_output_json = get_pretty(parsed_ner_labels)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8d8b9c0f", - "metadata": {}, - "outputs": [], - "source": [ - "parsed_ner_labels" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa189443", - "metadata": {}, - "outputs": [], - "source": [ - "# Cluster entities\n", - "clusters = cluster_with_cdist(\n", - " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", - " threshold=95,\n", - ")\n", - "\n", - "for cluster in clusters:\n", - " canonical = pick_canonical(cluster)\n", - " originals = [c[0] for c in cluster]\n", - " print(f\"Canonical: {canonical} -> {originals}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d76f9a89", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = clusters_to_canonical_entities(clusters)\n", - "\n", - "# Sort by label and canonical text\n", - "canonical_entities = sorted(\n", - " canonical_entities,\n", - " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", - ")\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9e65d30f", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare target filename\n", - "target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", - ")\n", - "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f42c8398", - "metadata": {}, - "outputs": [], - "source": [ - "# Save canonical entities to JSON in to-review folder to save the raw output\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save canonical entities to JSON in reviewing folder to make te review manually\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "2daee78a", - "metadata": {}, - "source": [ - "Make a pause and review the json manually for those wrong outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f5a1c1c9", - "metadata": {}, - "outputs": [], - "source": [ - "# Execute this cell to load reviewed canonical entities\n", - "reviewed_canonical_entities = load_json(\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", - "]\n", - "\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28359165", - "metadata": {}, - "outputs": [], - "source": [ - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "dfc64748", - "metadata": {}, - "source": [ - "# 2 - Feb. 2 (pdf)" - ] - }, - { - "cell_type": "markdown", - "id": "edf32104", - "metadata": {}, - "source": [ - "# Pipeline NER - Clusterization - Validation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bbafb4e9", - "metadata": {}, - "outputs": [], - "source": [ - "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/2 - Feb. 2 - DE ON, Kwan x 13944_response_1765836659235.json\"\n", - "\n", - "json_path = Path(json_path)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "679a5817", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "document = load_json(json_path)[\"document\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f640f548", - "metadata": {}, - "outputs": [], - "source": [ - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")\n", - "\n", - "# Get NER predictions\n", - "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", - "ner_output_json = get_pretty(parsed_ner_labels)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ce9cc2c1", - "metadata": {}, - "outputs": [], - "source": [ - "parsed_ner_labels" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a007f3c", - "metadata": {}, - "outputs": [], - "source": [ - "# Cluster entities\n", - "clusters = cluster_with_cdist(\n", - " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", - " threshold=95,\n", - ")\n", - "\n", - "for cluster in clusters:\n", - " canonical = pick_canonical(cluster)\n", - " originals = [c[0] for c in cluster]\n", - " print(f\"Canonical: {canonical} -> {originals}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c5ef7c49", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = clusters_to_canonical_entities(clusters)\n", - "\n", - "# Sort by label and canonical text\n", - "canonical_entities = sorted(\n", - " canonical_entities,\n", - " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", - ")\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b461fdab", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare target filename\n", - "target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", - ")\n", - "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "564fc2a4", - "metadata": {}, - "outputs": [], - "source": [ - "# Save canonical entities to JSON in to-review folder to save the raw output\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save canonical entities to JSON in reviewing folder to make te review manually\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "f8329a42", - "metadata": {}, - "source": [ - "Make a pause and review the json manually for those wrong outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ac09ef3", - "metadata": {}, - "outputs": [], - "source": [ - "# Execute this cell to load reviewed canonical entities\n", - "reviewed_canonical_entities = load_json(\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", - "]\n", - "\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9006d7ec", - "metadata": {}, - "outputs": [], - "source": [ - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "2f7a2ba5", - "metadata": {}, - "source": [ - "# Jul. 18 (pdf)" - ] - }, - { - "cell_type": "markdown", - "id": "83b4b16e", - "metadata": {}, - "source": [ - "# Pipeline NER - Clusterization - Validation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "431ecd13", - "metadata": {}, - "outputs": [], - "source": [ - "json_path = \"../../../resources/data/restricted/disambiguation-eval/files/Jul. 18- SALA IV- IPANAQUE VILLAR Carlos Hector s 5c nnapes_response_1765835513742.json\"\n", - "\n", - "json_path = Path(json_path)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "692fc5d5", - "metadata": {}, - "outputs": [], - "source": [ - "# Extract document\n", - "document = load_json(json_path)[\"document\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4176067e", - "metadata": {}, - "outputs": [], - "source": [ - "if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")\n", - "\n", - "# Get NER predictions\n", - "ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - "parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", - "ner_output_json = get_pretty(parsed_ner_labels)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dbf3644d", - "metadata": {}, - "outputs": [], - "source": [ - "parsed_ner_labels" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8cfda586", - "metadata": {}, - "outputs": [], - "source": [ - "# Cluster entities\n", - "clusters = cluster_with_cdist(\n", - " [f\"{v['aymurai_label']}:{v['text']}\" for v in parsed_ner_labels],\n", - " threshold=95,\n", - ")\n", - "\n", - "for cluster in clusters:\n", - " canonical = pick_canonical(cluster)\n", - " originals = [c[0] for c in cluster]\n", - " print(f\"Canonical: {canonical} -> {originals}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b0c6b557", - "metadata": {}, - "outputs": [], - "source": [ - "canonical_entities = clusters_to_canonical_entities(clusters)\n", - "\n", - "# Sort by label and canonical text\n", - "canonical_entities = sorted(\n", - " canonical_entities,\n", - " key=lambda ce: (ce.aymurai_label.lower(), ce.canonical_text.lower()),\n", - ")\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4443d7c9", - "metadata": {}, - "outputs": [], - "source": [ - "# Prepare target filename\n", - "target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(json_path))[0]\n", - ")\n", - "target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9de334ef", - "metadata": {}, - "outputs": [], - "source": [ - "# Save canonical entities to JSON in to-review folder to save the raw output\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/to-review/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save canonical entities to JSON in reviewing folder to make te review manually\n", - "save_json(\n", - " [ce.model_dump() | {\"entity_id\": None} for ce in canonical_entities],\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "c0bb69fb", - "metadata": {}, - "source": [ - "Make a pause and review the json manually for those wrong outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "76b7ddc8", - "metadata": {}, - "outputs": [], - "source": [ - "# Execute this cell to load reviewed canonical entities\n", - "reviewed_canonical_entities = load_json(\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewing/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\"\n", - ")\n", - "\n", - "# Convert reviewed entities back to CanonicalEntity objects\n", - "canonical_entities = [\n", - " CanonicalEntity.model_validate(entity) for entity in reviewed_canonical_entities\n", - "]\n", - "\n", - "canonical_entities = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in canonical_entities\n", - "]\n", - "\n", - "canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f5e15c9", - "metadata": {}, - "outputs": [], - "source": [ - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/reviewed/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")\n", - "\n", - "# Save reviewed canonical entities\n", - "save_json(\n", - " canonical_entities,\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted/\"\n", - " + target_filename\n", - " + \"-canonical-entities.json\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "555c7c5f", - "metadata": {}, - "source": [ - "# Start point of the old version of the pipeline" - ] - }, - { - "cell_type": "markdown", - "id": "ccbe21f4", - "metadata": {}, - "source": [ - "## CanonicalEntity extraction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd025617", - "metadata": {}, - "outputs": [], - "source": [ - "# Available models\n", - "# model = \"gpt-oss:20b\"\n", - "# model = \"llama3\"\n", - "model = \"phi4:14b\"\n", - "# model = \"llama3.1:8b\"\n", - "# model = \"gemma3:270m\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25284997", - "metadata": {}, - "outputs": [], - "source": [ - "# Sanity check\n", - "provider = OllamaLLMProvider(model=model)\n", - "provider.generate(\"hola, ¿cómo estás?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "33ca3dd3", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " stream_prompt = \"hola, ¿cómo estás?\"\n", - " pieces = []\n", - " for event in provider.stream(stream_prompt):\n", - " print(event.text, end=\"\", flush=True)\n", - " pieces.append(event.text)\n", - " print()\n", - " full_text = \"\".join(pieces)\n", - "\n", - "except Exception as exc:\n", - " print(\"La transmisión no está disponible:\", exc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c7ec7c6", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt = \"\"\"\n", - "Eres un asistente especializado en anonimización de sentencias judiciales.\n", - "Tu tarea es agrupar las menciones de entidades nombradas detectadas por un modelo de NER en **entidades canónicas** sin omitir ninguna mención válida.\n", - "Una **entidad canónica** es la representación única de una entidad real.\n", - "Agrupa todas las menciones textuales (aliases) que se refieren a una misma persona, documento, lugar, número, fecha, etc., aunque aparezcan con variantes ortográficas o abreviadas.\n", - "\n", - "# Reglas\n", - "- Usa solo información presente en el documento y en las menciones del NER; no inventes datos ni concluyas hechos no expresos.\n", - "- Toda mención listada por el NER debe evaluarse. Si representa una entidad real, inclúyela en alguna entidad canónica.\n", - "- Puede haber falsos positivos y/o negativos, menciones ambiguas o incompletas, por lo que debes evaluar cada mención cuidadosamente.\n", - "- Cada entidad canónica debe incluir:\n", - " - `aymurai_label` (etiqueta del NER, p. ej. \"PER\", \"DNI\", etc.).\n", - " - `canonical_text` (forma normalizada: nombres completos, fechas normalizadas, etc.).\n", - " - `aliases` (todas las menciones textuales relevantes).\n", - " - `attributes` (diccionario opcional con roles u otras notas, p. ej. {\"role\": \"Juez/a\"}).\n", - "\n", - "# Notas\n", - "- `aliases` debe conservar las menciones textuales tal como aparecen en el documento.\n", - "- `attributes` es especialmente útil para distinguir roles procesales. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - "\n", - "# Ejemplo\n", - "Q: Con fecha 3 de marzo de 2023, la Sra. Laura Beatriz Gómez, DNI 42.987.654, con domicilio en calle Falsa 123, Barrio Los Pinos, Villa Azul, denunció a su expareja, el Sr. Martín Alberto Rodríguez, DNI 27.654.321, con domicilio en calle Real 789, por hechos de violencia física, psicológica y amenazas con armas blancas.\n", - "A: ```json\n", - "[\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Laura Beatriz Gómez\",\n", - " \"aliases\": [\"Laura Beatriz Gómez\"],\n", - " \"attributes\": {\"role\": \"Denunciante\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"42987654\",\n", - " \"aliases\": [\"42.987.654\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DIRECCION\",\n", - " \"canonical_text\": \"calle Falsa 123\",\n", - " \"aliases\": [\"calle Falsa 123\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"Barrio Los Pinos, Villa Azul\",\n", - " \"aliases\": [\"Barrio Los Pinos, Villa Azul\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Martín Alberto Rodríguez\",\n", - " \"aliases\": [\"Martín Alberto Rodríguez\"],\n", - " \"attributes\": {\"role\": \"Denunciado\"}\n", - " },\n", - " {\n", - " \"aymurai_label\": \"DNI\",\n", - " \"canonical_text\": \"27654321\",\n", - " \"aliases\": [\"27.654.321\"]\n", - " },\n", - " {\n", - " \"aymurai_label\": \"LOC\",\n", - " \"canonical_text\": \"calle Real 789\",\n", - " \"aliases\": [\"calle Real 789\"]\n", - " }\n", - "]\n", - "```\n", - "\"\"\"\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6994e3c3", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template = \"\"\"\n", - "A continuación se proporciona un documento judicial y las menciones de entidades nombradas detectadas por un modelo de NER.\n", - "\n", - "# Documento\n", - "{document_text}\n", - "\n", - "# Menciones de entidades detectadas por el NER\n", - "{ner_output_json}\n", - "\n", - "# Instrucciones\n", - "1. Evalúa cada mención listada. Si representa una entidad real, inclúyela en la lista de aliases de alguna entidad canónica.\n", - "2. No omitas entidades válidas, alias con iniciales ni variantes abreviadas.\n", - "3. Solo fusiona menciones cuando exista evidencia clara de que se refieren a la misma entidad.\n", - "4. Normaliza canonical_text; mantén los alias tal como aparecen.\n", - "5. Utiliza attributes para indicar roles (p. ej. {{\"role\": \"Denunciante\"}}) o aclaraciones de desambiguación.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "349fa489", - "metadata": {}, - "outputs": [], - "source": [ - "def extract_canonical_entities(\n", - " doc_path: str, model: str = \"phi4:14b\"\n", - ") -> CanonicalEntities:\n", - " \"\"\"\n", - " Extract canonical entities from a document.\n", - "\n", - " Args:\n", - " doc_path (str): The path to the document.\n", - " model (str, optional): The model to use for extraction. Defaults to \"phi4:14b\".\n", - "\n", - " Raises:\n", - " ValueError: If the document is empty or not found.\n", - " ValueError: If the document summary is empty or not found.\n", - "\n", - " Returns:\n", - " CanonicalEntities: The extracted canonical entities.\n", - " \"\"\"\n", - " # Extract document\n", - " session = requests.Session()\n", - " document = call_extraction_api(session, Path(doc_path))\n", - " document = document.get(\"detail\", {}).get(\"document\")\n", - "\n", - " if not document:\n", - " raise ValueError(\"Document text is empty or not found.\")\n", - "\n", - " # Get NER predictions\n", - " ner_predictions = [get_predictions(paragraph) for paragraph in tqdm(document)]\n", - " parsed_ner_labels = parse_prediction_labels(ner_predictions)\n", - " ner_output_json = get_pretty(parsed_ner_labels)\n", - "\n", - " # Prepare user prompt\n", - " user_prompt = user_prompt_template.format(\n", - " document_text=\"\\n\".join(document).strip(),\n", - " ner_output_json=ner_output_json,\n", - " )\n", - "\n", - " # Get canonical entities from the model\n", - " provider = OllamaLLMProvider(model=model)\n", - " response = provider.generate(\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options={\"temperature\": 0, \"num_ctx\": 12_000},\n", - " format=CanonicalEntities.model_json_schema(),\n", - " )\n", - "\n", - " # Parse canonical entities from response\n", - " canonical_entities = [\n", - " output for output in json.loads(response.text)[\"canonical_entities\"]\n", - " ]\n", - "\n", - " canonical_entities = [\n", - " CanonicalEntity.model_validate(canonical_entity)\n", - " for canonical_entity in canonical_entities\n", - " ]\n", - "\n", - " return canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52c14551", - "metadata": {}, - "outputs": [], - "source": [ - "output_root = Path(DATA_ROOT) / \"preds\"\n", - "output_root.mkdir(parents=True, exist_ok=True)\n", - "\n", - "prompt_version = \"1\"\n", - "\n", - "timestamp = time.strftime(\"%y%m%d_%H%M\")\n", - "base_dir_name = f\"preds-{model}-pv{prompt_version}-{timestamp}\"\n", - "output_dir = output_root / base_dir_name\n", - "\n", - "output_dir.mkdir(parents=True, exist_ok=False)\n", - "print(f\"Saving canonical entities to {output_dir}\")\n", - "\n", - "for doc_path in documents:\n", - " print(f\"Processing document: {doc_path}\")\n", - "\n", - " try:\n", - " canonical_entities = extract_canonical_entities(doc_path, model=model)\n", - " print(f\"Extracted {len(canonical_entities)} canonical entities.\")\n", - "\n", - " target_filename = re.sub(\n", - " r\"\\s+|_\", \"-\", os.path.splitext(os.path.basename(doc_path))[0]\n", - " )\n", - " target_filename = re.sub(r\"-{2,}\", \"-\", target_filename)\n", - " target_path = output_dir / f\"{target_filename}.json\"\n", - "\n", - " save_json(\n", - " [\n", - " canonical_entity.model_dump()\n", - " | {\"entity_id\": canonical_entity.entity_id.hex}\n", - " for canonical_entity in canonical_entities\n", - " ],\n", - " str(target_path),\n", - " )\n", - "\n", - " except Exception as e:\n", - " print(f\"Error processing document {doc_path}: {e}\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai", - "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.10.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb b/notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb deleted file mode 100644 index 5cc52d55..00000000 --- a/notebooks/experiments/entity-disambiguation/07-LLM-entities-disambiguation.ipynb +++ /dev/null @@ -1,1713 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "64957296", - "metadata": {}, - "source": [ - "from previous notebook `06-pre-disambiguation-optimization` we found that the best hyperparameters combinations are:\n", - "- scorer: `token_set_ratio`\n", - "- score_cutoff: `70`\n", - "- processor: `light_normalizer`\n", - "\n", - "we now proceed to make the canonical entities disambiguation checked by an LLM and we give the task to the LLM to give a role to each canonical entity when it is related to a person." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb2b1f59", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext rich\n", - "\n", - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "607ea227", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import json\n", - "import os\n", - "import re\n", - "from more_itertools import unique_everseen\n", - "\n", - "from pathlib import Path\n", - "from typing import Iterable\n", - "import uuid\n", - "\n", - "import requests\n", - "\n", - "import numpy as np\n", - "\n", - "from aymurai.llm_providers import OllamaLLMProvider\n", - "from aymurai.meta.entities import CanonicalEntities, CanonicalEntity\n", - "from aymurai.utils.json_data import get_pretty, save_json, load_json\n", - "\n", - "import aymurai.evaluation.metrics as aymurai_metrics\n", - "from aymurai.utils.entity_disambiguation import build_canonical_entities\n", - "from aymurai.meta.api_interfaces import DocumentAnnotations" - ] - }, - { - "cell_type": "markdown", - "id": "7ec52f24", - "metadata": {}, - "source": [ - "## /document-extract endpoint output and new endpoint anonymizer/disambiguate" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c7c3b672", - "metadata": {}, - "outputs": [], - "source": [ - "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8000\")\n", - "ENDPOINT_DOCUMENT_EXTRACT = f\"{API_BASE_URL}/misc/document-extract\"\n", - "\n", - "DATA_ROOT = Path(\n", - " os.getenv(\n", - " \"DOCUMENT_DATA_ROOT\", \"../../../resources/data/restricted/disambiguation-eval/files\"\n", - " )\n", - ")\n", - "GOLD_JSON_ROOT = Path(\n", - " os.getenv(\n", - " \"GOLD_JSON_ROOT\",\n", - " \"../../../resources/data/restricted/disambiguation-eval/canonical-entities/manual-predicted\",\n", - " )\n", - ")\n", - "\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "JSON_EXTENSION = {\".json\"}\n", - "\n", - "REQUEST_TIMEOUT_S = float(os.getenv(\"DOCUMENT_REQUEST_TIMEOUT\", \"30\"))\n", - "\n", - "print(f\"Target endpoint: {ENDPOINT_DOCUMENT_EXTRACT}\")\n", - "print(f\"Data root: {DATA_ROOT.resolve()}\")\n", - "\n", - "LIMIT = int(os.getenv(\"DISAMBIGUATION_DOC_LIMIT\", \"0\")) # 0 = no limit\n", - "TARGET_LABELS = os.getenv(\"DISAMBIGUATION_TARGET_LABELS\", \"PER,DIRECCION\")\n", - "TARGET_LABELS = [label.strip() for label in TARGET_LABELS.split(\",\") if label.strip()]\n", - "\n", - "FUZZY_THRESHOLD = int(os.getenv(\"DISAMBIGUATION_THRESHOLD\", \"70\"))\n", - "FUZZY_SCORER = os.getenv(\"DISAMBIGUATION_SCORER\", \"token_set_ratio\")\n", - "FUZZY_PROCESSOR = os.getenv(\"DISAMBIGUATION_PROCESSOR\", \"light_normalizer\")\n", - "\n", - "print(f\"API: {API_BASE_URL}\")\n", - "print(f\"Data root: {DATA_ROOT}\")\n", - "print(f\"Target labels: {TARGET_LABELS or 'ALL'}\")\n", - "print(\n", - " f\"Fuzzy params: scorer={FUZZY_SCORER}, threshold={FUZZY_THRESHOLD}, processor={FUZZY_PROCESSOR}\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18a31502", - "metadata": {}, - "outputs": [], - "source": [ - "if not DATA_ROOT.exists():\n", - " raise FileNotFoundError(\n", - " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", - " )\n", - "\n", - "\n", - "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "\n", - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "if LIMIT > 0:\n", - " documents = documents[:LIMIT]\n", - "print(f\"Discovered {len(documents)} documents.\")\n", - "\n", - "gold_jsons = discover_documents(GOLD_JSON_ROOT, JSON_EXTENSION)\n", - "if LIMIT > 0:\n", - " gold_jsons = gold_jsons[:LIMIT]\n", - "print(f\"Discovered {len(gold_jsons)} gold JSON files.\")" - ] - }, - { - "cell_type": "markdown", - "id": "dcaed02f", - "metadata": {}, - "source": [ - "## Functions for API endpoints usage" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "464e9132", - "metadata": {}, - "outputs": [], - "source": [ - "from aymurai.experiments.entity_disambiguation.runner import (\n", - " call_extraction_api as extract_document,\n", - ")\n", - "\n", - "def predict_paragraphs(paragraphs: list[str]) -> list[dict]:\n", - " endpoint = f\"{API_BASE_URL}/anonymizer/predict\"\n", - " predictions = []\n", - " for paragraph in paragraphs:\n", - " response = requests.post(endpoint, json={\"text\": paragraph}, timeout=60)\n", - " response.raise_for_status()\n", - " predictions.append(response.json())\n", - " return predictions" - ] - }, - { - "cell_type": "markdown", - "id": "f2aa5d15", - "metadata": {}, - "source": [ - "## Canonical Entity extraction with ***LLM inference***" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8220314e", - "metadata": {}, - "outputs": [], - "source": [ - "# Available models\n", - "models = [\n", - " \"phi4:14b\",\n", - " \"gpt-oss:20b\",\n", - " \"llama3\",\n", - " \"llama3.1:8b\",\n", - " \"gemma3:270m\",\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "82f8d1a2", - "metadata": {}, - "outputs": [], - "source": [ - "# # Sanity check\n", - "# provider = OllamaLLMProvider(model=models[0])\n", - "# provider.generate(\"hola, ¿cómo estás?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f40ef8ed", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_1 = \"\"\"\n", - "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", - "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", - "\n", - "# Material de Trabajo\n", - "Para tu análisis, recibirás:\n", - "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", - "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", - "\n", - "# Tus Tareas\n", - "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", - "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", - "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", - "\n", - "# Reglas de Salida\n", - "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", - "- `canonical_text`: El nombre más completo y formal encontrado.\n", - "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Defensor/a de Cámara\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", - "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", - "\n", - "# Estructura de Respuesta (JSON)\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Alias Principal\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\n", - "# Ejemplo de Input\n", - "Contexto: \"...la declaración del Sr. Juan Carlos Ruiz ante este Juzgado. El imputado Ruiz negó los cargos. Por su parte, la Dra. Elena Sosa, fiscal de la causa, solicitó...\"\n", - "Entidades Candidatas:\n", - "[{\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Juan Carlos Ruiz\",\n", - " \"aliases\": [\n", - " \"Juan Carlos Ruiz\",\n", - " \"Ruiz\"\n", - " ]\n", - "},\n", - "{\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Elena Sosa\",\n", - " \"aliases\": [\n", - " \"Elena Sosa\"\n", - " ]\n", - "}]\n", - "\n", - "# Ejemplo de Output\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Juan Carlos Ruiz\",\n", - " \"aliases\": [\n", - " \"Juan Carlos Ruiz\",\n", - " \"Ruiz\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Imputado\"\n", - " }\n", - " },\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Elena Sosa\",\n", - " \"aliases\": [\n", - " \"Elena Sosa\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Fiscal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40c5f5ae", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_2 = \"\"\"\n", - "Eres un asistente experto en análisis legal y anonimización de documentos judiciales.\n", - "Tu función es auditar y enriquecer una lista de **Entidades Canónicas (Personas)** pre-agrupadas.\n", - "\n", - "# Material de Trabajo\n", - "Para tu análisis, recibirás:\n", - "1. **Contexto de la Causa:** Una selección de párrafos del documento original donde se detectaron las menciones.\n", - "2. **Entidades Canónicas:** Una lista de grupos de aliases donde cada grupo representa a una única persona física, en teoría.\n", - "\n", - "# Tus Tareas\n", - "1. **Validación Semántica:** Usa el contexto para confirmar que los aliases de un grupo realmente refieren a la misma persona física. Si detectas que un grupo contiene personas distintas (ej. homónimos o familiares), sepáralos.\n", - "2. **Consolidación:** Si una persona aparece en dos grupos diferentes (ej. \"Juan Pérez\" en uno y \"Juan Alberto Pérez\" en otro), únelos en una única entidad canónica.\n", - "3. **Asignación de Rol:** Identifica el rol procesal (Juez/a, Denunciante, Imputado/a, Víctima, Abogado/a, etc.) analizando cómo interactúa la persona en los párrafos provistos.\n", - "\n", - "# Reglas de Salida\n", - "- Devuelve **únicamente** un objeto JSON que contenga el array de entidades procesadas.\n", - "- `canonical_text`: El nombre más completo y formal encontrado.\n", - "- `attributes`: Diccionario con el campo `\"role\"`. Si no se detecta rol, que el diccionario attributes quede vacío. Entre ellos pueden incluirse:\n", - " * \"Denunciante\"\n", - " * \"Denunciado/a\"\n", - " * \"Víctima\"\n", - " * \"Juez/a\"\n", - " * \"Defensor/a de Cámara\"\n", - " * \"Fiscal\"\n", - " * \"Abogado/a\"\n", - " * \"Perito/a\"\n", - " * \"Testigo\"\n", - " Debes asignar un rol tal cual se escribe en esa lista, no inventes nuevos roles.\n", - "- Mantén los `aliases` originales tal cual aparecen en el texto.\n", - "\n", - "# Estructura de Respuesta (JSON)\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Alias Principal\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3f2848b", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template_1 = \"\"\"\n", - "A continuación se proporcionan fragmentos relevantes de un documento judicial y una lista de entidades (personas) pre-clusterizadas por similitud de texto.\n", - "\n", - "# Contexto de la causa, párrafos con alguna mención a las personas:\n", - "{document_text}\n", - "\n", - "# Entidades Pre-clusterizadas a validar:\n", - "{canonical_entities}\n", - "\n", - "# Instrucciones:\n", - "1. **Validación de Aliases:** Analiza si los aliases dentro de cada entidad canónica pertenecen realmente a la misma persona según los párrafos de contexto. \n", - " - Si un alias pertenece a una persona distinta (ej. un familiar con nombre similar), sepáralo en una nueva entidad.\n", - " - Si dos grupos de entidades canónicas refieren a la misma persona, fusiónalos.\n", - "2. **Identificación de Roles:** Extrae el rol procesal de cada persona basándote en el contexto (ej: Juez/a, Fiscal, Denunciante, Imputado/a, Víctima, Abogado/a, Perito/a, Testigo).\n", - "3. **Normalización:** Define el `canonical_text` como el nombre más completo y formal que aparezca en los aliases, si este trabajo ya está bien hecho no lo modifiques.\n", - "4. **Formato:** Devuelve la lista final de entidades en el formato JSON solicitado en el system prompt, asegurándote de no omitir a nadie que sea relevante.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7d9365ca", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_3 = \"\"\"\n", - "Eres un asistente experto en desambiguación de entidades judiciales.\n", - "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", - "\n", - "### Tu Tarea:\n", - "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", - "2. **Limpiar Aliases:** Elimina prefijos (Dr., Sra., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", - "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", - "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - " - **Nota:** No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica rol, usa `null`.\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve exclusivamente un array de objetos:\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Alias Principal\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0f7bf0f7", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_4 = \"\"\"\n", - "Eres un asistente experto en desambiguación de entidades judiciales.\n", - "Recibirás fragmentos de una causa y un JSON de entidades canónicas pre-agrupadas.\n", - "\n", - "### Tu Tarea:\n", - "1. **Validar y Fusionar:** Verifica que cada entidad represente a una única persona real. Si dos grupos de entidades refieren a la misma persona, fusiónalos.\n", - "2. **Limpiar Aliases:** Elimina prefijos (Dr., Dra., Dres., Sra., Sr., Lic., etc.) o sufijos de los nombres (Asesor, Administrativo, etc.). Los aliases deben ser solo nombres propios.\n", - "3. **Filtrar:** Elimina cualquier entidad que no sea una persona física.\n", - "4. **Asignar Rol:** Clasifica cada entidad según el contexto usando **únicamente** esta lista estandarizada:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - " \n", - " **Instrucciones críticas de rol:**\n", - " - Si una persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y el contexto no indica que es Juez, Fiscal o Defensor oficial, asígnale el rol de **\"Abogado/a\"**.\n", - " - No añadas información extra (ej. \"Juez de Cámara\" debe ser solo \"Juez/a\"). Si no se identifica un rol de la lista, usa `null`.\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve exclusivamente un array de objetos:\n", - "[\n", - " {\n", - " \"canonical_text\": \"Nombre Completo Normalizado\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9bed0ca", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_5 = \"\"\"\n", - "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", - "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", - "4. **Limpiar:** En `canonical_text` y `aliases`, elimina otras palabras que no sean nombres propios.\n", - "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ac77829", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_6 = \"\"\"\n", - "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", - "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", - "4. **Fusionar:** Si dos entidades candidatas refieren a la misma persona, devuélvelas como una sola.\n", - "5. No modifiques los `canonical_text` ni los `aliases` originales.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "26f1b81b", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_7 = \"\"\"\n", - "Eres un extractor de roles legales. Tu única función es auditar una lista de entidades pre-agrupadas basándote en fragmentos de texto judicial.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Filtrar:** Elimina cualquier entidad que no sea una persona física (ej: calles, instituciones, leyes, fechas).\n", - "2. **Asignar Rol:** Identifica el rol procesal usando EXCLUSIVAMENTE esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "3. **Regla \"Dr/a\":** Si la persona es mencionada como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito (como Juez o Fiscal), asígnale \"Abogado/a\".\n", - "4. **Manejo de Siglas e Iniciales:** Ten en cuenta que las personas pueden ser mencionadas por sus iniciales (ej: \"M.L.\" para \"Martín López\"). Si el contexto permite confirmar que unas iniciales refieren a una persona ya identificada, trátalas como un alias de esa misma entidad.\n", - "5. **Fusionar:** Si dos entidades candidatas refieren a la misma persona (ya sea por nombre completo, apellido solo o iniciales), devuélvelas como una sola entidad unificada.\n", - "6. **Integridad:** No modifiques los `canonical_text` ni los `aliases` originales; mantén la literalidad de lo extraído por el NER.\n", - "\n", - "Devuelve exclusivamente un array de objetos:\n", - "[\n", - " {\n", - " \"canonical_text\": \"Nombre Completo Normalizado\",\n", - " \"aliases\": [\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol Procesal\"\n", - " }\n", - " }\n", - "]\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55aff26f", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_8 = \"\"\"\n", - "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", - " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", - " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", - "2. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", - "3. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", - "4. **Asignación de Rol:** Identifica el rol procesal basándote EXCLUSIVAMENTE en esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "5. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve un array de objetos con esta estructura:\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Nombre de la entidad,\n", - " \"aliases\": [\n", - " \"Nombre de la entidad\",\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol de la lista o null\"\n", - " }\n", - " }\n", - "]\n", - "No incluyas el campo 'context' en tu respuesta final.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "822e348e", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_9 = \"\"\"\n", - "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Asignación de Rol:** Identifica el rol procesal basándote en las ventanas de contexto dentro de cada entidad canónica en la parte de `attributes`['context']\n", - " Elija un rol EXCLUSIVAMENTE en esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "2. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", - "3. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", - " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", - " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", - "4. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", - "5. **Limpieza:** Limpiá los `aliases` y el `canonical_text` si los mismos tienen otras palabras, pero respeta que estén escritos de igual manera a que sus ventanas de contexto.\n", - " Te dejo un ejemplo de esta instrucción.\n", - " Vos recibís:\n", - " {\n", - " \"canonical_text\": \"por DREXLER, JORGE\",\n", - " \"aliases\": [\n", - " \"por DREXLER, JORGE\",\n", - " \"Jorge Drexler\"\n", - " ],\n", - " \"attributes\": {\n", - " \"context\": [\n", - " \"Fdo. por DREXLER, JORGE - JUEZ DE CÁMARA el mismo cita en el documento 56 que Juan es culpable\",\n", - " \"es acaso Jorge Drexler el juez designado para esta causa\"\n", - " ]\n", - " }\n", - " \n", - " Debés entregar:\n", - " {\n", - " \"canonical_text\": \"DREXLER, JORGE\",\n", - " \"aliases\": [\n", - " \"DREXLER, JORGE\",\n", - " \"Jorge Drexler\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Juez/a\"\n", - " }\n", - " \n", - "6. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve un array de objetos con esta estructura:\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Nombre de la entidad,\n", - " \"aliases\": [\n", - " \"Nombre de la entidad\",\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol de la lista o null\"\n", - " }\n", - " }\n", - "]\n", - "\n", - "No incluyas el campo 'context' en tu respuesta final.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fc51a3ea", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_10 = \"\"\"\n", - "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", - " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", - " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", - "2. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", - "3. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", - "4. **Asignación de Rol:** Identifica el rol procesal basándote EXCLUSIVAMENTE en esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "5. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", - "6. **Limpieza:** No modifiques la literalidad de los `aliases` ni del `canonical_text`.\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve un array de objetos con esta estructura:\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Nombre de la entidad,\n", - " \"aliases\": [\n", - " \"Nombre de la entidad\",\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol de la lista o null\"\n", - " }\n", - " }\n", - "]\n", - "No incluyas el campo 'context' en tu respuesta final.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb600013", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_11 = \"\"\"\n", - "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y corregir una lista de personas pre-agrupadas basándote en su contexto.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Validación de Límites (Boundaries):** El modelo NER a veces incluye palabras adyacentes que no son parte del nombre (ej: \"por\", \"Fdo.\", \"Dres\", \"dijo\", \"con\"). Tu tarea es corregir los `aliases` y el `canonical_text` para que contengan ÚNICAMENTE el nombre de la persona, pero manteniendo la literalidad exacta (mayúsculas, tildes, comas) de cómo está escrito en el fragmento de `context`.\n", - "2. **Fusión y Desambiguación:** - Si dos grupos de entidades refieren a la misma persona física, fusiónalos.\n", - " - Si una persona aparece por sus iniciales (ej: \"M.L.\"), asóciala como alias del nombre completo si el contexto lo confirma.\n", - "3. **Filtrado:** Elimina entidades que no sean personas físicas.\n", - "4. **Asignación de Rol:** Usa exclusivamente: \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "5. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, el rol es \"Abogado/a\".\n", - "6. **Limpieza:** No modifiques la literalidad de los `aliases` ni del `canonical_text`.\n", - "\n", - "### Formato de Salida (JSON):\n", - "[\n", - " {\n", - " \"entity_id\": \"ID original\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Nombre exacto extraído del texto\",\n", - " \"aliases\": [\n", - " \"Nombre exacto extraído del texto (Igual al canonical)\",\n", - " \"Otros aliases exactos del texto...\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol de la lista o null\"\n", - " }\n", - " }\n", - "]\n", - "**IMPORTANTE:** El primer elemento de `aliases` debe ser idéntico al `canonical_text`. No incluyas el campo 'context' en la respuesta.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ca7b8755", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_DIR = \"\"\"\n", - "Eres un auditor experto en desambiguación de domicilios y direcciones en documentos judiciales. Tu tarea es validar y consolidar una lista de direcciones pre-agrupadas basándote en su contexto.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Consolidación Geográfica:** - Si dos entidades refieren a la misma ubicación física, fusiónalas en una sola entidad. \n", - " - Considera que una dirección puede estar escrita de forma completa en un alias (ej: \"Av. Rivadavia 123, CABA\") y de forma abreviada en otro (ej: \"Rivadavia 123\"). \n", - " - Trata las variantes ortográficas o abreviaturas (Av. vs Avenida, Piso vs P., Dpto vs Departamento) como aliases de la misma ubicación.\n", - "2. **Filtrado:** Elimina entidades que no sean direcciones postales o domicilios físicos (ej: correos electrónicos, URLs, nombres de juzgados o menciones abstractas como \"el domicilio constituido\").\n", - "\n", - "### Formato de Salida (JSON):\n", - "[\n", - " {\n", - " \"entity_id\": \"ID original\",\n", - " \"aymurai_label\": \"DIRECCION\",\n", - " \"canonical_text\": \"Dirección más completa extraída del texto\",\n", - " \"aliases\": [\n", - " \"Dirección más completa (Igual al canonical)\",\n", - " \"Variante 1\",\n", - " \"Variante 2\"\n", - " ],\n", - " \"attributes\": []\n", - " }\n", - "]\n", - "**IMPORTANTE:** No incluyas el campo 'context' en la respuesta.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d9feb9f", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template_DIR= \"\"\"\n", - "Se proporciona la lista de entidades (direcciones) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su validez y si hay otras entidades de direcciones que corresponden a esa.\n", - "\n", - "# Entidades Pre-clusterizadas a validar:\n", - "{canonical_entities}\n", - "\n", - "Procesa la lista siguiendo las instrucciones de filtrado y fusión.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b0d4ad36", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template_PER = \"\"\"\n", - "Se proporciona la lista de entidades (personas) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su rol y validez.\n", - "\n", - "# Entidades Pre-clusterizadas a validar:\n", - "{canonical_entities}\n", - "\n", - "Procesa la lista siguiendo las instrucciones de filtrado, fusión y asignación de roles.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "efc48244", - "metadata": {}, - "outputs": [], - "source": [ - "def pre_cluster(\n", - " paragraphs: list[dict],\n", - " target_labels: list[str] = None,\n", - " threshold: int = 70,\n", - ") -> CanonicalEntities:\n", - "\n", - " predictions = DocumentAnnotations(data=paragraphs)\n", - "\n", - " labels = [\n", - " label for paragraph in predictions.data for label in (paragraph.labels or [])\n", - " ]\n", - "\n", - " target_set = {label.strip() for label in target_labels} if target_labels else None\n", - "\n", - " canonical_entities = build_canonical_entities(\n", - " labels,\n", - " target_labels=target_set,\n", - " threshold=threshold,\n", - " )\n", - "\n", - " return canonical_entities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1431d116", - "metadata": {}, - "outputs": [], - "source": [ - "import copy\n", - "\n", - "def add_canonical_entities_context(\n", - " predictions: list[dict], \n", - " entities: list[dict],\n", - " context_window_length: int | None = 120,\n", - " max_context_snippets: int | None = 5,\n", - ") -> list[dict]:\n", - " \"\"\"\n", - " Creates a deep copy of entities and adds context. \n", - " \n", - " Args:\n", - " predictions: List of prediction dictionaries from the API.\n", - " entities: List of canonical entities.\n", - " context_window_length: Length of the window around the label. If None, full paragraph is used.\n", - " target_label: The specific label to filter. If None, all labels are considered.\n", - " \"\"\"\n", - " # 1. Deep copy to protect original variable\n", - " entities_with_context = copy.deepcopy(entities)\n", - "\n", - " # 2. Process each entity\n", - " for entity in entities_with_context:\n", - " if 'attributes' not in entity or entity['attributes'] is None:\n", - " entity['attributes'] = {}\n", - " if 'context' not in entity['attributes']:\n", - " entity['attributes']['context'] = []\n", - " \n", - " aliases = [a for a in entity.get('aliases', [])]\n", - "\n", - " alias_to_snippets = {alias: [] for alias in aliases}\n", - " all_snippets_ordered = []\n", - "\n", - " # 3. Iterate through predictions\n", - " for pred in predictions:\n", - " if not pred.get('labels'):\n", - " continue\n", - "\n", - " doc_text = pred.get('document', '')\n", - " \n", - " for label in pred.get('labels', []):\n", - " if entity.get('aymurai_label') and label['attrs']['aymurai_label'] != entity.get('aymurai_label'):\n", - " continue\n", - "\n", - " alias_name = label['attrs']['aymurai_alt_text']\n", - " if alias_name not in aliases:\n", - " continue\n", - " \n", - " # Handle Window vs Full Paragraph\n", - " if context_window_length is None:\n", - " snippet = doc_text\n", - " else:\n", - " start = label['start_char']\n", - " end = label['end_char']\n", - "\n", - " window_start = max(0, start - context_window_length)\n", - " window_end = min(len(doc_text), end + context_window_length)\n", - "\n", - " # Expand to word boundaries to avoid truncating leading/trailing words.\n", - " while window_start > 0 and not doc_text[window_start - 1].isspace():\n", - " window_start -= 1\n", - " while window_end < len(doc_text) and not doc_text[window_end - 1].isspace():\n", - " window_end += 1\n", - "\n", - " snippet = doc_text[window_start:window_end]\n", - "\n", - " clean_snippet = \" \".join(snippet.split())\n", - "\n", - " if clean_snippet not in alias_to_snippets[alias_name]:\n", - " alias_to_snippets[alias_name].append(clean_snippet)\n", - " if clean_snippet not in all_snippets_ordered:\n", - " all_snippets_ordered.append(clean_snippet)\n", - "\n", - " baseline = []\n", - " for alias in aliases:\n", - " snips = alias_to_snippets.get(alias, [])\n", - " if snips:\n", - " baseline.append(snips[0])\n", - " \n", - " baseline = list(unique_everseen(baseline))\n", - " \n", - " remaining = [s for s in all_snippets_ordered if s not in baseline]\n", - " merged = baseline + remaining\n", - "\n", - " effective_limit = max(max_context_snippets, len(baseline))\n", - " entity['attributes']['context'] = merged[:effective_limit]\n", - "\n", - " return entities_with_context\n", - "\n", - "def validate_canonical_entities(\n", - " canonical_entities_raw: list[CanonicalEntity], target_label: str | None = None\n", - ") -> list[CanonicalEntity]:\n", - "\n", - " if target_label:\n", - " canonical_entities_val = [\n", - " e for e in canonical_entities_raw if e.aymurai_label == target_label\n", - " ]\n", - " else:\n", - " canonical_entities_val = canonical_entities_raw\n", - "\n", - " canonical_entities_val = [\n", - " CanonicalEntity.model_validate(canonical_entity)\n", - " for canonical_entity in canonical_entities_val\n", - " ]\n", - "\n", - " canonical_entities_val = [\n", - " entity.model_dump() | {\"entity_id\": uuid.uuid4()}\n", - " for entity in canonical_entities_val\n", - " ]\n", - "\n", - " return canonical_entities_val" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fded1133", - "metadata": {}, - "outputs": [], - "source": [ - "tokenizers = {\n", - " \"phi4:14b\": \"microsoft/phi-4\",\n", - " \"gpt-oss:20b\": \"EleutherAI/gpt-neox-20b\",\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f93c8263", - "metadata": {}, - "outputs": [], - "source": [ - "from transformers import AutoTokenizer\n", - "\n", - "def get_phi4_tokens(system_prompt, user_prompt, model):\n", - " \n", - " try:\n", - " tokenizer = AutoTokenizer.from_pretrained(tokenizers[model])\n", - " \n", - " messages = [\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ]\n", - " try:\n", - " full_text = tokenizer.apply_chat_template(\n", - " messages, \n", - " tokenize=False, \n", - " add_generation_prompt=True\n", - " )\n", - " except Exception:\n", - " full_text = f\"{system_prompt}\\n{user_prompt}\"\n", - " \n", - " tokens = tokenizer.encode(full_text)\n", - " return len(tokens)\n", - " \n", - " except Exception as e:\n", - " print(f\"Error loading tokenizer {tokenizers[model]}: {e}\")\n", - " return int(len((system_prompt + user_prompt).split()) * 1.4)\n", - "\n", - "\n", - "\n", - "def llm_infer_canonical_entities(\n", - " system_prompt: str,\n", - " user_prompt_template: str,\n", - " model: str,\n", - " document_path: Path,\n", - " gold_path: Path,\n", - " context_window_length: int | None = 120,\n", - " model_context: int = 9_500,\n", - " token_limit_frac: float = 2/3,\n", - " target_label: str = \"PER\",\n", - " temperature: int = 0,\n", - " decompose_by: int | None = 0,\n", - ") -> dict:\n", - " \n", - " \"\"\"\n", - " Infers canonical entities using an LLM by providing context windows \n", - " around detected entities and pre-clusterization results.\n", - " \"\"\"\n", - "\n", - " # 1. Filename cleaning\n", - " clean_base_name = document_path.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", - " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", - "\n", - " print(f\"----- Processing document: {target_filename} -----\")\n", - "\n", - " # 2. Extract document text via API\n", - " session = requests.Session()\n", - " api_response = extract_document(\n", - " session,\n", - " file_path = Path(document_path),\n", - " endpoint=f\"{API_BASE_URL}/misc/document-extract\",\n", - " timeout_s=300,\n", - " )\n", - " \n", - " document_paragraphs = api_response.get(\"detail\", {}).get(\"document\")\n", - "\n", - " if not document_paragraphs:\n", - " raise ValueError(\"Document text is empty or not found for {target_filename}.\")\n", - "\n", - " # 3. Get the pre-clustered entities with context from API calls\n", - " predictions = predict_paragraphs(document_paragraphs)\n", - " \n", - " canonical_entities_pre_cluster = pre_cluster(\n", - " paragraphs=predictions,\n", - " target_labels=[target_label],\n", - " threshold=70,\n", - " )\n", - "\n", - " canonical_entities_pre_cluster = validate_canonical_entities(\n", - " canonical_entities_raw=canonical_entities_pre_cluster,\n", - " target_label=target_label\n", - " )\n", - "\n", - " canonical_entities_with_context = add_canonical_entities_context(\n", - " predictions=predictions,\n", - " entities=canonical_entities_pre_cluster,\n", - " context_window_length=context_window_length,\n", - " )\n", - "\n", - " # 4. Clean entities to save tokens\n", - " canonical_entities_prompt = [\n", - " {\n", - " k: v\n", - " for k, v in ce.items()\n", - " if k in (\"canonical_text\",\"aliases\",\"attributes\")\n", - " }\n", - " for ce in canonical_entities_with_context\n", - " ]\n", - "\n", - " token_limit = int(model_context*token_limit_frac)\n", - "\n", - " # 5. Find Optimal Batch Size if decompose_by is None\n", - " if decompose_by is None:\n", - " current_batch_size = len(canonical_entities_prompt)\n", - " print(f\"{'-' * 60}\")\n", - " print(\"Searching for optimal batch size...\")\n", - " print(f\"Starting with {current_batch_size} entities.\")\n", - " \n", - " while current_batch_size > 0:\n", - " # Test with the first N entities\n", - " test_batch = canonical_entities_prompt[:current_batch_size]\n", - " test_prompt = user_prompt_template.format(\n", - " canonical_entities=get_pretty(test_batch)\n", - " )\n", - " \n", - " num_tokens = get_phi4_tokens(system_prompt=system_prompt, user_prompt=test_prompt, model=model)\n", - " \n", - " if num_tokens <= token_limit:\n", - " decompose_by = current_batch_size\n", - " print(f\"{'-' * 60}\")\n", - " print(f\"Optimal batch size found: {decompose_by} entities ({num_tokens} tokens).\")\n", - " break\n", - " \n", - " current_batch_size -= 1\n", - " \n", - " if not decompose_by: # Safety check if even 1 entity is too large\n", - " print(\"\\nEven a single entity exceeds the token limit.\")\n", - " return None\n", - "\n", - " # 6. Prepare batches. If decompose_by is 0 or less, we put all entities in one single list (one batch)\n", - " if decompose_by <= 0:\n", - " entity_batches = [canonical_entities_prompt]\n", - " else:\n", - " entity_batches = [\n", - " canonical_entities_prompt[i : i + decompose_by]\n", - " for i in range(0, len(canonical_entities_prompt), decompose_by)\n", - " ]\n", - "\n", - " all_raw_outputs = []\n", - " all_user_prompts = []\n", - "\n", - " # 7. Iterate through batches\n", - " for batch_index, batch in enumerate(entity_batches):\n", - " print(f\"{'-' * 60}\")\n", - " print(f\"Processing batch {batch_index + 1}/{len(entity_batches)}.\")\n", - " \n", - " user_prompt = user_prompt_template.format(\n", - " canonical_entities=get_pretty(batch),\n", - " )\n", - " \n", - " all_user_prompts.append(user_prompt)\n", - "\n", - " # 7.A Token Validation\n", - " len_tokens = get_phi4_tokens(system_prompt=system_prompt, user_prompt=user_prompt, model=model)\n", - " if len_tokens > model_context:\n", - " print(f\"Batch {batch_index} too large ({len_tokens} tokens).\")\n", - " print(\"Skipping batch.\")\n", - " continue\n", - " else:\n", - " print(f\"Total batch tokens: {len_tokens} within model context ({model_context}).\")\n", - "\n", - " # 7.B LLM Inference\n", - " provider = OllamaLLMProvider(model=model)\n", - " response = provider.generate(\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options={\"temperature\": temperature, \"num_ctx\": model_context},\n", - " format=CanonicalEntities.model_json_schema(),\n", - " )\n", - "\n", - " # 7.C Parse and collect results\n", - " batch_entities = json.loads(response.text).get(\"canonical_entities\", [])\n", - " all_raw_outputs.extend(batch_entities)\n", - "\n", - "\n", - " canonical_entities_llm = validate_canonical_entities(\n", - " canonical_entities_raw=all_raw_outputs\n", - " )\n", - "\n", - " # 8. Load Gold Standard for reference\n", - " canonical_entities_gold = load_json(json_file_path=gold_path)\n", - "\n", - " print(f\"{'-' * 60}\")\n", - " print(f\"Summary for {target_filename}:\")\n", - " print(f\"- Pre-cluster entities: {len(canonical_entities_pre_cluster)}\")\n", - " print(f\"- LLM generated entities: {len(canonical_entities_llm)}\")\n", - " print(f\"- Gold standard entities: {len(canonical_entities_gold)}\")\n", - "\n", - " # 9. Map the canonical entities in the predictions documents te return the right format for the front-end\n", - "\n", - " predictions_llm = copy.deepcopy(predictions)\n", - " \n", - " new_ids_map = {}\n", - "\n", - " for document in predictions_llm:\n", - " if not document.get('labels'):\n", - " continue\n", - "\n", - " for label in document.get('labels'):\n", - " label_text = label['attrs']['aymurai_alt_text']\n", - " if (\n", - " label['attrs']['canonical_entity_id'] is None\n", - " and len(label['attrs']['aymurai_label_subclass']) == 0\n", - " ):\n", - " pred_label = label['attrs']['aymurai_label']\n", - " for ce in canonical_entities_llm:\n", - " ce_label = ce.get(\"aymurai_label\")\n", - " if pred_label == ce_label:\n", - " entity_id = ce.get(\"entity_id\")\n", - " attributes = ce.get(\"attributes\") or {}\n", - " role = attributes.get(\"role\")\n", - " aliases = ce.get(\"aliases\") or []\n", - "\n", - " if any(\n", - " str(alias).strip() == str(label_text).strip()\n", - " for alias in aliases\n", - " ):\n", - " label['attrs']['canonical_entity_id'] = entity_id\n", - " if ce_label == \"PER\" and role is not None:\n", - " label['attrs']['aymurai_label_subclass'].append(role)\n", - " break\n", - "\n", - " if label['attrs']['canonical_entity_id'] is None:\n", - " key = (label['attrs']['aymurai_label'], str(label_text).strip())\n", - " if key not in new_ids_map:\n", - " new_ids_map[key] = uuid.uuid4()\n", - "\n", - " label['attrs']['canonical_entity_id'] = new_ids_map[key]\n", - "\n", - " # 10. Return structured results\n", - " return {\n", - " \"target_filename\": target_filename,\n", - " \"pre_cluster_json\": canonical_entities_pre_cluster,\n", - " \"canonical_entities_with_context\": canonical_entities_with_context,\n", - " \"llm_output_json\": canonical_entities_llm,\n", - " \"gold_standard_json\": canonical_entities_gold,\n", - " \"system_prompt\": system_prompt,\n", - " \"user_prompts\": all_user_prompts,\n", - " \"len_tokens\": len_tokens,\n", - " \"predictions\": predictions_llm\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "586b1ddd", - "metadata": {}, - "outputs": [], - "source": [ - "def get_document_paths(document_check: Path, pre_clusterization_root: Path) -> dict:\n", - " \"\"\"\n", - " Generates the pre-cluster and gold JSON paths for a given document.\n", - " \n", - " Args:\n", - " document_check (Path): The original document Path object.\n", - " pre_clusterization_root (Path): The root directory for pre-clusterization data.\n", - " \n", - " Returns:\n", - " dict: A dictionary containing the 'pre_cluster_path' and 'gold_path'.\n", - " \"\"\"\n", - " # 1. Prepare target filename\n", - " clean_base_name = document_check.stem.replace(\"_\", \"-\").replace(\" \", \"-\")\n", - " target_filename = re.sub(r\"-{2,}\", \"-\", clean_base_name).strip(\"-\")\n", - "\n", - " # 2. Define subdirectories\n", - " pre_cluster_dir = pre_clusterization_root / 'best-pre-clusterization-per'\n", - " gold_dir = pre_clusterization_root / 'gold-jsons'\n", - "\n", - " # 3. Build final paths\n", - " pre_cluster_json_path = pre_cluster_dir / f\"{target_filename}-ner-preds-pre-cluster.json\"\n", - " gold_json_path = gold_dir / f\"{target_filename}-canonical-entities-gold.json\"\n", - "\n", - " return {\n", - " \"target_filename\": target_filename,\n", - " \"pre_cluster_path\": pre_cluster_json_path,\n", - " \"gold_path\": gold_json_path\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3f419ede", - "metadata": {}, - "outputs": [], - "source": [ - "PRE_CLUSTERIZATION_ROOT = GOLD_JSON_ROOT.parent / \"pre-clusterization\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "90229978", - "metadata": {}, - "outputs": [], - "source": [ - "import shutil\n", - "\n", - "def run_llm_grid_search(\n", - " models: list,\n", - " system_prompts: dict,\n", - " user_prompt_templates: dict,\n", - " context_window_lengths: list,\n", - " model_context_lengths: list,\n", - " temperatures: list,\n", - " token_limit_frac: float,\n", - " documents: list[Path],\n", - " target_label: str,\n", - " base_output_path: Path,\n", - " decompose_by: int | None = 0\n", - "):\n", - " \"\"\"\n", - " Runs a grid search over hyperparameters using nested loops.\n", - " \"\"\"\n", - "\n", - " # To keep track of all results and find the best\n", - " all_combinations_results = []\n", - "\n", - " for model in models:\n", - " for system_prompt_name, system_prompt in system_prompts.items():\n", - " for user_prompt_template_name, user_prompt_template in user_prompt_templates.items():\n", - " for context_window_length in context_window_lengths:\n", - " for model_context in model_context_lengths:\n", - " for temperature in temperatures:\n", - " \n", - " print(\n", - " f\"\\nRunning grid search with model: {model}, \"\n", - " f\"context_window_length: {context_window_length}, \"\n", - " f\"model_context: {model_context}, \"\n", - " f\"temperature: {temperature}\"\n", - " )\n", - "\n", - " # 1. Create a descriptive folder name with the cleaned names\n", - " combo_name = (\n", - " f\"model-{model}-{system_prompt_name}-\"\n", - " f\"{user_prompt_template_name}-context_window-{context_window_length}-\"\n", - " f\"model_context-{model_context}-temperature-{temperature}\"\n", - " )\n", - "\n", - " combo_dir = base_output_path / combo_name\n", - " combo_dir.mkdir(parents=True, exist_ok=True)\n", - "\n", - " metrics_results = {}\n", - " metrics_file_path = (\n", - " combo_dir / f\"evaluation_metrics_{target_label}.json\"\n", - " )\n", - "\n", - " # List to collect scores for this specific combination\n", - " current_combo_scores = []\n", - "\n", - " for document in documents:\n", - " \n", - " # Get document paths\n", - " paths = get_document_paths(document, PRE_CLUSTERIZATION_ROOT) # We use this because the gold_json is in an specific folder\n", - "\n", - " # Run LLM inference\n", - " llm_response_dict = llm_infer_canonical_entities(\n", - " system_prompt=system_prompt,\n", - " user_prompt_template=user_prompt_template,\n", - " model=model,\n", - " document_path=document,\n", - " gold_path=paths['gold_path'], # When adding this to the experiment runner, make sure to pass the correct gold path\n", - " context_window_length=context_window_length,\n", - " model_context=model_context,\n", - " target_label=target_label,\n", - " temperature=temperature,\n", - " decompose_by=decompose_by,\n", - " token_limit_frac=token_limit_frac\n", - " )\n", - " # Extract predictions\n", - " ce_llm = llm_response_dict['llm_output_json']\n", - " \n", - " # Validate and prepare for saving\n", - " ce_llm = [\n", - " CanonicalEntity.model_validate(entity)\n", - " for entity in ce_llm\n", - " ]\n", - "\n", - " ce_llm = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in ce_llm\n", - " ]\n", - "\n", - " # Save the result as the llm-pred.json\n", - " target_filename = llm_response_dict['target_filename']\n", - " llm_filename = f\"{target_filename}-llm-pred.json\"\n", - " llm_output_path = combo_dir / llm_filename\n", - "\n", - " save_json(\n", - " file_path=llm_output_path, json_data=ce_llm\n", - " )\n", - "\n", - " # Extract gold standard\n", - " ce_gold = llm_response_dict['gold_standard_json']\n", - " \n", - " # Validate and prepare for saving\n", - " ce_gold = [\n", - " CanonicalEntity.model_validate(entity)\n", - " for entity in ce_gold\n", - " ]\n", - "\n", - " ce_gold = [\n", - " entity.model_dump() | {\"entity_id\": entity.entity_id.hex}\n", - " for entity in ce_gold\n", - " ]\n", - "\n", - " len_llm = len(ce_llm)\n", - " len_gold = len(ce_gold)\n", - "\n", - " # Evaluate metrics\n", - " score, metrics = aymurai_metrics.evaluate_disambiguation(\n", - " gold_json=ce_gold,\n", - " pred_json=ce_llm,\n", - " target_label=target_label,\n", - " )\n", - " current_combo_scores.append(score)\n", - " doc_id = target_filename\n", - " metrics_results[doc_id] = {\n", - " \"label\": target_label,\n", - " \"metric_value\": score,\n", - " \"detailed_metrics\": metrics,\n", - " \"len_llm_predictions\": len_llm,\n", - " \"len_gold_standard\": len_gold\n", - " }\n", - "\n", - " # --- Calculate Average for this combination ---\n", - " avg_score = (\n", - " np.mean(current_combo_scores) if current_combo_scores else 0.0\n", - " )\n", - "\n", - " # Add average to the results file for this folder\n", - " final_output = {\n", - " \"average_combination_score\": avg_score,\n", - " \"document_details\": metrics_results,\n", - " }\n", - "\n", - " with open(metrics_file_path, \"w\") as f:\n", - " json.dump(final_output, f, indent=4)\n", - "\n", - " # Store combination info for final ranking\n", - " all_combinations_results.append(\n", - " {\n", - " \"name\": combo_name,\n", - " \"score\": avg_score,\n", - " \"params\": {\n", - " \"model\": model,\n", - " \"system_prompt\": system_prompt_name,\n", - " \"user_prompt_template\": user_prompt_template_name,\n", - " \"context_window_length\": context_window_length,\n", - " \"model_context\": model_context\n", - " },\n", - " }\n", - " )\n", - "\n", - " # Save all combinations results in a summary file\n", - " summary_file_path = (\n", - " base_output_path / f\"results_summary_{target_label}.json\"\n", - " )\n", - " with open(summary_file_path, \"w\") as f:\n", - " json.dump(all_combinations_results, f, indent=4)\n", - "\n", - " # --- Find the best combination ---\n", - " best_combo = max(all_combinations_results, key=lambda x: x[\"score\"])\n", - "\n", - " print(f\"\\nGrid Search for {target_label} completed.\")\n", - "\n", - " # We now copy the best combination folder to a new location with a standardized name\n", - " src = Path(base_output_path) / best_combo[\"name\"]\n", - " # Combine the parent destination path with the new folder name\n", - " new_name = f\"best-llm-pred-{target_label.lower()}\"\n", - " dest = Path(base_output_path.parent) / new_name\n", - " \n", - " try:\n", - " # copytree creates the destination directory with the 'new_name'\n", - " shutil.copytree(src, dest)\n", - " print(f\"Successfully copied '{src.name}' to '{dest}'\")\n", - " except FileExistsError:\n", - " print(f\"Error: A folder named '{new_name}' already exists in '{base_output_path.parent}'\")\n", - " except Exception as e:\n", - " print(f\"An error occurred: {e}\") \n", - "\n", - " return best_combo" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5110dc60", - "metadata": {}, - "outputs": [], - "source": [ - "models = [\n", - " \"phi4:14b\",\n", - " \"gpt-oss:20b\",\n", - " \"llama3\",\n", - " \"llama3.1:8b\",\n", - " \"gemma3:270m\",\n", - "]\n", - "\n", - "system_prompts = {\n", - " \"system_prompt_1\": system_prompt_1,\n", - " \"system_prompt_2\": system_prompt_2,\n", - " \"system_prompt_3\": system_prompt_3,\n", - " \"system_prompt_4\": system_prompt_4,\n", - " \"system_prompt_5\": system_prompt_5,\n", - " \"system_prompt_6\": system_prompt_6,\n", - " \"system_prompt_7\": system_prompt_7,\n", - " \"system_prompt_8\": system_prompt_8,\n", - " \"system_prompt_9\": system_prompt_9,\n", - " \"system_prompt_10\": system_prompt_10,\n", - " \"system_prompt_11\": system_prompt_11\n", - "}\n", - "\n", - "\n", - "user_prompt_templates = {\n", - " \"user_prompt_template_PER\": user_prompt_template_PER,\n", - "}\n", - "\n", - "context_window_lengths = list(range(80, 200, 10))\n", - "\n", - "model_context_lengths = [7_500, 8_000, 8_500, 9_000, 9_500]\n", - "\n", - "temperatures = list(np.arange(0,0.3,0.1))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7949d277", - "metadata": {}, - "outputs": [], - "source": [ - "document_check = documents[1]\n", - "paths = get_document_paths(document_check, PRE_CLUSTERIZATION_ROOT)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6ff4f5a", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict = llm_infer_canonical_entities(\n", - " system_prompt=system_prompts['system_prompt_9'],\n", - " user_prompt_template=user_prompt_templates['user_prompt_template_PER'],\n", - " model=models[0],\n", - " document_path=document_check,\n", - " gold_path=paths[\"gold_path\"],\n", - " context_window_length=120,\n", - " model_context=9_500,\n", - " token_limit_frac=2/3,\n", - " temperature=0,\n", - " target_label=\"PER\",\n", - " decompose_by=None\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60a2356a", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict = llm_infer_canonical_entities(\n", - " system_prompt=system_prompt_DIR,\n", - " user_prompt_template=user_prompt_template_DIR,\n", - " model=models[0],\n", - " document_path=document_check,\n", - " gold_path=paths[\"gold_path\"],\n", - " context_window_length=None,\n", - " model_context=9_500,\n", - " token_limit_frac=2/3,\n", - " temperature=0,\n", - " target_label=\"DIRECCION\",\n", - " decompose_by=None\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "690c940d", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict['predictions']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e8bcb07", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict['llm_output_json']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "135c1f36", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict['canonical_entities_with_context']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "59f003a9", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict[\"pre_cluster_json\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22e29b34", - "metadata": {}, - "outputs": [], - "source": [ - "llm_response_dict[\"gold_standard_json\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2845f2f5", - "metadata": {}, - "outputs": [], - "source": [ - "print(llm_response_dict[\"user_prompts\"][0])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "38e587f3", - "metadata": {}, - "outputs": [], - "source": [ - "print(llm_response_dict['system_prompt'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4fe27195", - "metadata": {}, - "outputs": [], - "source": [ - "models = [\n", - " \"phi4:14b\",\n", - "]\n", - "\n", - "system_prompts = {\n", - " \"system_prompt_9\": system_prompt_9\n", - "}\n", - "\n", - "\n", - "user_prompt_templates = {\n", - " \"user_prompt_template_PER\": user_prompt_template_PER,\n", - "}\n", - "\n", - "context_window_lengths = [120]\n", - "\n", - "model_context_lengths = [9_500]\n", - "\n", - "temperatures = [0]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a32b4244", - "metadata": {}, - "outputs": [], - "source": [ - "top_result = run_llm_grid_search(\n", - " models=models,\n", - " system_prompts=system_prompts,\n", - " user_prompt_templates=user_prompt_templates,\n", - " context_window_lengths=context_window_lengths,\n", - " model_context_lengths=model_context_lengths,\n", - " temperatures=temperatures,\n", - " documents=documents,\n", - " target_label=\"PER\",\n", - " base_output_path=GOLD_JSON_ROOT.parent / \"llm-grid-search-results\",\n", - " token_limit_frac=2/3,\n", - " decompose_by=None\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "86da6a45", - "metadata": {}, - "outputs": [], - "source": [ - "print(\n", - " f\"The best hyperparameter combination is '{top_result['name']}' \"\n", - " f\"with an average score of {top_result['score']:.4f}.\"\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai", - "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.10.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb b/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb deleted file mode 100644 index 6eed83f1..00000000 --- a/notebooks/experiments/entity-disambiguation/08-disambiguation-endpoint-smoke.ipynb +++ /dev/null @@ -1,399 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b3a62938", - "metadata": {}, - "source": [ - "# Disambiguation endpoint smoke test\n", - "\n", - "This notebook runs the full flow using the API endpoints:\n", - "1) document extraction\n", - "2) NER prediction per paragraph\n", - "3) disambiguation\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2edb7397", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext rich" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e468613", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import json\n", - "import os\n", - "from pathlib import Path\n", - "\n", - "import requests" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "80f525f3", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template_PER = \"\"\"\n", - "Se proporciona la lista de entidades (personas) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su rol y validez.\n", - "\n", - "# Entidades Pre-clusterizadas a validar:\n", - "{canonical_entities}\n", - "\n", - "Procesa la lista siguiendo las instrucciones de filtrado, fusión y asignación de roles.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6c8f2ce2", - "metadata": {}, - "outputs": [], - "source": [ - "user_prompt_template_DIR = \"\"\"\n", - "Se proporciona la lista de entidades (direcciones) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su validez y si hay otras entidades de direcciones que corresponden a esa.\n", - "\n", - "# Entidades Pre-clusterizadas a validar:\n", - "{canonical_entities}\n", - "\n", - "Procesa la lista siguiendo las instrucciones de filtrado y fusión.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1aa9140e", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_PER = \"\"\"\n", - "Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Asignación de Rol:** Identifica el rol procesal basándote en las ventanas de contexto dentro de cada entidad canónica en la parte de `attributes`['context']\n", - " Elija un rol EXCLUSIVAMENTE en esta lista:\n", - " - \"Denunciante\", \"Denunciado/a\", \"Víctima\", \"Juez/a\", \"Defensor/a de Cámara\", \"Asesor/a Tutelar\", \"Fiscal\", \"Abogado/a\", \"Perito/a\", \"Testigo\".\n", - "2. **Regla \"Dr/a\":** Si se menciona como \"Dr.\", \"Dra.\" o \"Dres.\" y no hay otro cargo explícito, asígnale el rol \"Abogado/a\".\n", - "3. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona.\n", - " - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos.\n", - " - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo.\n", - "4. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes).\n", - "5. **Limpieza:** Limpiá los `aliases` y el `canonical_text` si los mismos tienen otras palabras, pero respeta que estén escritos de igual manera a que sus ventanas de contexto.\n", - " Te dejo un ejemplo de esta instrucción.\n", - " Vos recibís:\n", - " {\n", - " \"canonical_text\": \"por DREXLER, JORGE\",\n", - " \"aliases\": [\n", - " \"por DREXLER, JORGE\",\n", - " \"Jorge Drexler\"\n", - " ],\n", - " \"attributes\": {\n", - " \"context\": [\n", - " \"Fdo. por DREXLER, JORGE - JUEZ DE CÁMARA el mismo cita en el documento 56 que Juan es culpable\",\n", - " \"es acaso Jorge Drexler el juez designado para esta causa\"\n", - " ]\n", - " }\n", - "\n", - " Debés entregar:\n", - " {\n", - " \"canonical_text\": \"DREXLER, JORGE\",\n", - " \"aliases\": [\n", - " \"DREXLER, JORGE\",\n", - " \"Jorge Drexler\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Juez/a\"\n", - " }\n", - "\n", - "6. **Manejo de Iniciales:** Identifica si las siglas (ej: \"M.L.\") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma.\n", - "\n", - "### Formato de Salida (JSON):\n", - "Devuelve un array de objetos con esta estructura:\n", - "[\n", - " {\n", - " \"entity_id\": \"optional-unique-id\",\n", - " \"aymurai_label\": \"PER\",\n", - " \"canonical_text\": \"Nombre de la entidad\",\n", - " \"aliases\": [\n", - " \"Nombre de la entidad\",\n", - " \"Alias 1\",\n", - " \"Alias 2\"\n", - " ],\n", - " \"attributes\": {\n", - " \"role\": \"Rol de la lista o null\"\n", - " }\n", - " }\n", - "]\n", - "\n", - "No incluyas el campo 'context' en tu respuesta final.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d67ad4f2", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompt_DIR = \"\"\"\n", - "Eres un auditor experto en desambiguación de domicilios y direcciones en documentos judiciales. Tu tarea es validar y consolidar una lista de direcciones pre-agrupadas basándote en su contexto.\n", - "\n", - "### Instrucciones Estrictas:\n", - "1. **Consolidación Geográfica:** - Si dos entidades refieren a la misma ubicación física, fusiónalas en una sola entidad.\n", - " - Considera que una dirección puede estar escrita de forma completa en un alias (ej: \"Av. Rivadavia 123, CABA\") y de forma abreviada en otro (ej: \"Rivadavia 123\").\n", - " - Trata las variantes ortográficas o abreviaturas (Av. vs Avenida, Piso vs P., Dpto vs Departamento) como aliases de la misma ubicación.\n", - "2. **Filtrado:** Elimina entidades que no sean direcciones postales o domicilios físicos (ej: correos electrónicos, URLs, nombres de juzgados o menciones abstractas como \"el domicilio constituido\").\n", - "\n", - "### Formato de Salida (JSON):\n", - "[\n", - " {\n", - " \"entity_id\": \"ID original\",\n", - " \"aymurai_label\": \"DIRECCION\",\n", - " \"canonical_text\": \"Dirección más completa extraída del texto\",\n", - " \"aliases\": [\n", - " \"Dirección más completa (Igual al canonical)\",\n", - " \"Variante 1\",\n", - " \"Variante 2\"\n", - " ],\n", - " \"attributes\": []\n", - " }\n", - "]\n", - "**IMPORTANTE:** No incluyas el campo 'context' en la respuesta.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "43dcd972", - "metadata": {}, - "outputs": [], - "source": [ - "SYSTEM_PROMPTS = {\n", - " \"PER\": system_prompt_PER,\n", - " \"DIRECCION\": system_prompt_DIR,\n", - "}\n", - "\n", - "USER_PROMPT_TEMPLATES = {\n", - " \"PER\": user_prompt_template_PER,\n", - " \"DIRECCION\": user_prompt_template_DIR,\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a54d0295", - "metadata": {}, - "outputs": [], - "source": [ - "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8000\")\n", - "DATA_ROOT = Path(\n", - " os.getenv(\n", - " \"DISAMBIGUATION_DATA_ROOT\",\n", - " \"../../../resources/data/restricted/disambiguation-eval/files\",\n", - " )\n", - ")\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "\n", - "LIMIT = int(os.getenv(\"DISAMBIGUATION_DOC_LIMIT\", \"0\")) # 0 = no limit\n", - "TARGET_LABELS = os.getenv(\"DISAMBIGUATION_TARGET_LABELS\")\n", - "\n", - "print(f\"API: {API_BASE_URL}\")\n", - "print(f\"Data root: {DATA_ROOT}\")\n", - "print(f\"Target labels: {TARGET_LABELS or 'All the targets that have the prompt set'}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "58e9d69e", - "metadata": {}, - "outputs": [], - "source": [ - "from aymurai.experiments.entity_disambiguation.runner import (\n", - " call_extraction_api as extract_document,\n", - ")\n", - "\n", - "def discover_documents(root: Path, extensions: set[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "def predict_paragraphs(paragraphs: list[str]) -> list[dict]:\n", - " endpoint = f\"{API_BASE_URL}/anonymizer/predict\"\n", - " predictions = []\n", - " for paragraph in paragraphs:\n", - " response = requests.post(\n", - " endpoint, json={\"text\": paragraph}, params={\"use_cache\": False}, timeout=60\n", - " )\n", - " response.raise_for_status()\n", - " predictions.append(response.json())\n", - " return predictions\n", - "\n", - "\n", - "def disambiguate(\n", - " predictions: list[dict], use_custom_prompts: bool = True\n", - ") -> list[dict]:\n", - " endpoint = f\"{API_BASE_URL}/anonymizer/disambiguate\"\n", - "\n", - " payload = {\"paragraphs\": predictions}\n", - "\n", - " if use_custom_prompts:\n", - " custom_prompts_list = []\n", - "\n", - " for label in SYSTEM_PROMPTS.keys():\n", - " custom_prompts_list.append(\n", - " {\n", - " \"label\": label,\n", - " \"system\": SYSTEM_PROMPTS.get(label, \"\"),\n", - " \"user\": USER_PROMPT_TEMPLATES.get(label, \"\"),\n", - " }\n", - " )\n", - "\n", - " payload[\"custom_prompts\"] = custom_prompts_list\n", - "\n", - " else:\n", - " payload[\"custom_prompts\"] = []\n", - "\n", - " params = {\"use_cache\": False}\n", - "\n", - " if TARGET_LABELS:\n", - " params[\"target_labels\"] = TARGET_LABELS\n", - "\n", - " else:\n", - " params[\"target_labels\"] = []\n", - "\n", - " response = requests.post(\n", - " endpoint,\n", - " params=params,\n", - " json=payload,\n", - " timeout=600,\n", - " )\n", - "\n", - " response.raise_for_status()\n", - " return response.json()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "72f3d427", - "metadata": {}, - "outputs": [], - "source": [ - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "if LIMIT > 0:\n", - " documents = documents[:LIMIT]\n", - "\n", - "print(f\"Found {len(documents)} documents\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d844f2c8", - "metadata": {}, - "outputs": [], - "source": [ - "for doc_path in documents[1:2]:\n", - " print(f\"\\n=== {doc_path.name} ===\")\n", - " session = requests.Session()\n", - " document = extract_document(\n", - " session,\n", - " endpoint=f\"{API_BASE_URL}/misc/document-extract\",\n", - " file_path=doc_path,\n", - " timeout_s=300,\n", - " )\n", - " paragraphs = document[\"detail\"][\"document\"]\n", - " print(f\"Paragraphs: {len(paragraphs)}\")\n", - "\n", - " predictions = predict_paragraphs(paragraphs)\n", - " print(f\"Predictions: {len(predictions)}\")\n", - "\n", - " disambiguated = disambiguate(predictions=predictions, use_custom_prompts=False)\n", - " print(f\"NER prdictions: {len(disambiguated.get('data', []))}\")\n", - " print(json.dumps(disambiguated, indent=2, ensure_ascii=False))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f65c14ab", - "metadata": {}, - "outputs": [], - "source": [ - "predictions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0eb0561f", - "metadata": {}, - "outputs": [], - "source": [ - "disambiguated" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e7296e8a", - "metadata": {}, - "outputs": [], - "source": [ - "for pred in disambiguated.get(\"data\", []):\n", - " labels = pred.get(\"labels\", [])\n", - " if labels:\n", - " for label in labels:\n", - " attrs = label.get(\"attrs\", {})\n", - " if attrs.get(\"aymurai_label\") == \"FECHA\":\n", - " print(\n", - " label.get(\"text\"),\n", - " attrs.get(\"aymurai_label_subclass\"),\n", - " attrs.get(\"canonical_entity_id\"),\n", - " )" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai", - "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.10.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 69e90d7d63df072307fc1a4d2f5820fe15cc7f2f Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:33:51 -0300 Subject: [PATCH 056/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20notebooks/expe?= =?UTF-8?q?riments/llm-providers=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../llm-providers/01-ollama-provider.ipynb | 200 ------------------ 1 file changed, 200 deletions(-) delete mode 100644 notebooks/experiments/llm-providers/01-ollama-provider.ipynb diff --git a/notebooks/experiments/llm-providers/01-ollama-provider.ipynb b/notebooks/experiments/llm-providers/01-ollama-provider.ipynb deleted file mode 100644 index 216292c4..00000000 --- a/notebooks/experiments/llm-providers/01-ollama-provider.ipynb +++ /dev/null @@ -1,200 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "dcfd3a57", - "metadata": {}, - "source": [ - "# `OllamaLLMProvider`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9890c8fb", - "metadata": {}, - "outputs": [], - "source": [ - "import asyncio\n", - "\n", - "from rich.pretty import pprint\n", - "\n", - "from aymurai.llm_providers import OllamaLLMProvider" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "85317574", - "metadata": {}, - "outputs": [], - "source": [ - "MODEL_NAME = \"llama3.2:3b\"\n", - "SYSTEM_PROMPT = \"Eres un asistente legal especializado en anonimización y desambiguación de entidades.\"\n", - "\n", - "provider = OllamaLLMProvider(model=MODEL_NAME, system_prompt=SYSTEM_PROMPT)\n", - "provider" - ] - }, - { - "cell_type": "markdown", - "id": "94e43f16", - "metadata": {}, - "source": [ - "## Chunking" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "53afac4f", - "metadata": {}, - "outputs": [], - "source": [ - "sample_text = (\n", - " \"La jueza fijó audiencia para la próxima semana y solicitó a las partes presentar \"\n", - " \"documentación adicional relacionada con la causa. El objetivo es determinar si existieron \"\n", - " \"violaciones al protocolo vigente y establecer medidas de protección para las víctimas.\"\n", - ")\n", - "\n", - "chunks = provider.chunk_text(sample_text, max_tokens=10, overlap=0)\n", - "for chunk in chunks:\n", - " print(f\"Chunk {chunk.index}: {chunk.token_count} tokens\")\n", - " pprint(chunk)" - ] - }, - { - "cell_type": "markdown", - "id": "1aa4d182", - "metadata": {}, - "source": [ - "## Generation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f1ce3b9b", - "metadata": {}, - "outputs": [], - "source": [ - "prompt = (\n", - " \"Resume en español el siguiente extracto de un expediente judicial y menciona los actores involucrados:\"\n", - " + sample_text\n", - ")\n", - "\n", - "try:\n", - " summary_response = provider.generate(prompt)\n", - " print(summary_response.text)\n", - " pprint(summary_response.metadata)\n", - "\n", - "except Exception as exc:\n", - " print(\"No se pudo contactar a Ollama:\", exc)" - ] - }, - { - "cell_type": "markdown", - "id": "da8f9c60", - "metadata": {}, - "source": [ - "## Streaming" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4ac718bd", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " stream_prompt = \"Explica en detalle el procedimiento legal descrito.\"\n", - " pieces = []\n", - " for event in provider.stream(stream_prompt):\n", - " print(event.text, end=\"\", flush=True)\n", - " pieces.append(event.text)\n", - " print()\n", - " full_text = \"\".join(pieces)\n", - "\n", - "except Exception as exc:\n", - " print(\"La transmisión no está disponible:\", exc)" - ] - }, - { - "cell_type": "markdown", - "id": "async-section", - "metadata": {}, - "source": [ - "## Async calls" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "async-demo", - "metadata": {}, - "outputs": [], - "source": [ - "async def demo_async_generate():\n", - " try:\n", - " result = await provider.async_generate(\"Di un resumen breve del texto\")\n", - " print(result.text)\n", - " except Exception as exc:\n", - " print(\"No se pudo contactar a Ollama (async):\", exc)\n", - "\n", - "\n", - "await demo_async_generate()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "72c5c551", - "metadata": {}, - "outputs": [], - "source": [ - "async def demo_async_stream():\n", - " try:\n", - " parts = []\n", - " async for event in provider.async_stream(\"Explica el caso con más detalle\"):\n", - " print(event.text, end=\"\", flush=True)\n", - " parts.append(event.text)\n", - " print()\n", - " except Exception as exc:\n", - " print(\"La transmisión async no está disponible:\", exc)\n", - "\n", - "\n", - "await demo_async_stream()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "67ede506", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai (3.10.19)", - "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.10.19" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From aa888667d2cfe749558078f5b701af3b4b265bb3 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:36:44 -0300 Subject: [PATCH 057/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20notebooks/expe?= =?UTF-8?q?riments/summarization=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../01-summarization-benchmark.ipynb | 735 -------- .../02-statistical-analysis.ipynb | 1651 ----------------- .../03-summarization-endpoint.ipynb | 170 -- .../04-dspy-prompt-optimization.ipynb | 955 ---------- 4 files changed, 3511 deletions(-) delete mode 100644 notebooks/experiments/summarization/01-summarization-benchmark.ipynb delete mode 100644 notebooks/experiments/summarization/02-statistical-analysis.ipynb delete mode 100644 notebooks/experiments/summarization/03-summarization-endpoint.ipynb delete mode 100644 notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb diff --git a/notebooks/experiments/summarization/01-summarization-benchmark.ipynb b/notebooks/experiments/summarization/01-summarization-benchmark.ipynb deleted file mode 100644 index e2a6558a..00000000 --- a/notebooks/experiments/summarization/01-summarization-benchmark.ipynb +++ /dev/null @@ -1,735 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f900ed31", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "%load_ext rich" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "03359440", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "import mimetypes\n", - "import os\n", - "import time\n", - "from pathlib import Path\n", - "from typing import Iterable\n", - "\n", - "import ollama\n", - "import requests\n", - "from IPython.display import Markdown\n", - "from ollama import ChatResponse\n", - "from tqdm import tqdm\n", - "\n", - "from aymurai.utils.json_data import load_json, save_json\n", - "from aymurai.utils.yaml_data import load_yaml" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6297e02c", - "metadata": {}, - "outputs": [], - "source": [ - "os.environ[\"OLLAMA_KEEP_ALIVE\"] = \"0\"\n", - "print(\"OLLAMA_KEEP_ALIVE set to 0. Models will unload immediately after use.\")" - ] - }, - { - "cell_type": "markdown", - "id": "1d59c9c9", - "metadata": {}, - "source": [ - "## Documents" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28110bf0", - "metadata": {}, - "outputs": [], - "source": [ - "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8899\")\n", - "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", - "DATA_ROOT = Path(\n", - " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", - ")\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", - "\n", - "print(f\"Target endpoint: {ENDPOINT}\")\n", - "print(f\"Data root: {DATA_ROOT.resolve()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3fb80b7", - "metadata": {}, - "outputs": [], - "source": [ - "if not DATA_ROOT.exists():\n", - " raise FileNotFoundError(\n", - " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", - " )\n", - "\n", - "\n", - "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "\n", - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "print(f\"Discovered {len(documents)} documents.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aebe1738", - "metadata": {}, - "outputs": [], - "source": [ - "def call_extraction_api(\n", - " session: requests.Session, file_path: Path\n", - ") -> dict[str, object]:\n", - " payload: dict[str, object] = {\n", - " \"path\": str(file_path),\n", - " \"status\": \"failure\",\n", - " \"status_code\": None,\n", - " \"elapsed_s\": None,\n", - " \"detail\": None,\n", - " }\n", - "\n", - " if not file_path.exists():\n", - " payload[\"detail\"] = \"File does not exist\"\n", - " return payload\n", - "\n", - " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", - " files = {\n", - " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", - " }\n", - "\n", - " try:\n", - " start = time.perf_counter()\n", - " response = session.post(\n", - " ENDPOINT,\n", - " files=files,\n", - " timeout=REQUEST_TIMEOUT,\n", - " )\n", - " elapsed = time.perf_counter() - start\n", - " except requests.RequestException as exc:\n", - " payload[\"detail\"] = f\"Request failed: {exc}\"\n", - " return payload\n", - " finally:\n", - " files[\"file\"][1].close()\n", - "\n", - " payload[\"status_code\"] = response.status_code\n", - " payload[\"elapsed_s\"] = elapsed\n", - "\n", - " try:\n", - " response_body = response.json()\n", - " except ValueError:\n", - " response_body = {\"raw\": response.text[:500]}\n", - "\n", - " if response.ok:\n", - " payload[\"status\"] = \"success\"\n", - " payload[\"detail\"] = {\n", - " \"document_id\": response_body.get(\"document_id\"),\n", - " \"document\": response_body.get(\"document\", []),\n", - " }\n", - " else:\n", - " payload[\"detail\"] = response_body\n", - "\n", - " return payload" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f04a9d2", - "metadata": {}, - "outputs": [], - "source": [ - "doc_path = documents[0]\n", - "extracted_document = call_extraction_api(requests.Session(), doc_path)[\"detail\"]\n", - "document = \"\\n\".join(extracted_document[\"document\"])\n", - "print(document)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb930b69", - "metadata": {}, - "outputs": [], - "source": [ - "extracted_documents = []\n", - "\n", - "for doc_path in tqdm(documents):\n", - " session = requests.Session()\n", - " extracted_document = call_extraction_api(session, doc_path)\n", - " extracted_documents.append(\n", - " {\n", - " \"doc_path\": os.path.basename(doc_path),\n", - " **extracted_document,\n", - " }\n", - " )\n", - " session.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ce7ea84c", - "metadata": {}, - "outputs": [], - "source": [ - "len(extracted_documents)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a4fee57", - "metadata": {}, - "outputs": [], - "source": [ - "extracted_documents[-1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a11c0245", - "metadata": {}, - "outputs": [], - "source": [ - "# Filter out empty extracted documents\n", - "extracted_documents = [\n", - " doc for doc in extracted_documents if len(doc[\"detail\"][\"document\"])\n", - "]\n", - "len(extracted_documents)" - ] - }, - { - "cell_type": "markdown", - "id": "e509e467", - "metadata": {}, - "source": [ - "## LLMs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "00604dcd", - "metadata": {}, - "outputs": [], - "source": [ - "MODELS = [\n", - " # Llama\n", - " \"llama3.1:8b\",\n", - " \"llama3.2:3b\",\n", - " # Gemma\n", - " \"gemma3:4b\",\n", - " \"gemma3n:e4b\",\n", - " \"gemma3:12b\",\n", - " # Qwen\n", - " \"qwen3:8b\",\n", - " \"qwen3:14b\",\n", - " # Phi\n", - " \"phi3:3.8b\",\n", - " \"phi4:14b\",\n", - " # DeepSeek\n", - " \"deepseek-r1:8b\",\n", - " \"deepseek-r1:14b\",\n", - " # GPT\n", - " \"gpt-oss:20b\",\n", - "]" - ] - }, - { - "cell_type": "markdown", - "id": "7669a65c", - "metadata": {}, - "source": [ - "### System Prompts" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f1adce13", - "metadata": {}, - "outputs": [], - "source": [ - "system_prompts = load_yaml(\"/resources/llm/summarization.yml\")[\"system-prompts\"]\n", - "system_prompts" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4dfe7899", - "metadata": {}, - "outputs": [], - "source": [ - "# Function to get chat response from the model\n", - "def get_chat_response(\n", - " system_prompt: str,\n", - " user_prompt: str,\n", - " model: str = \"llama3.2:3b\",\n", - " options: dict = {\"num_ctx\": 16_384, \"num_predict\": 4096},\n", - ") -> ChatResponse:\n", - " \"\"\"\n", - " Get chat response from the model.\n", - "\n", - " Args:\n", - " system_prompt (str): The system prompt.\n", - " user_prompt (str): The prompt from the user.\n", - " model (str, optional): The model to use. Defaults to \"llama3.2:3b\".\n", - " options (dict, optional): The options for the chat.\n", - " Defaults to {\"num_ctx\": 16_384, \"num_predict\": 4096}.\n", - "\n", - " Returns:\n", - " ChatResponse: The response from the chat.\n", - " \"\"\"\n", - " response = ollama.chat(\n", - " model=model,\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_prompt},\n", - " ],\n", - " options=options,\n", - " )\n", - "\n", - " return response" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c8016bd", - "metadata": {}, - "outputs": [], - "source": [ - "iter_documents = iter(extracted_documents)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e8677785", - "metadata": {}, - "outputs": [], - "source": [ - "document = next(iter_documents)\n", - "document = \"\\n\".join(document[\"detail\"][\"document\"])\n", - "print(document)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "034bd672", - "metadata": {}, - "outputs": [], - "source": [ - "# Define system prompt and get chat response\n", - "system_prompt = system_prompts[\"baseline\"]\n", - "\n", - "# Get chat response\n", - "chat_response = get_chat_response(system_prompt, document)\n", - "print(chat_response.message.content)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "918dc835", - "metadata": {}, - "outputs": [], - "source": [ - "# Define system prompt and get chat response\n", - "system_prompt = system_prompts[\"task-specific\"]\n", - "\n", - "# Get chat response\n", - "chat_response = get_chat_response(system_prompt, document)\n", - "print(chat_response.message.content)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4f4b55f3", - "metadata": {}, - "outputs": [], - "source": [ - "# Define prompt parameters\n", - "INFORMATION = \"\"\"\n", - " - Hechos relevantes (qué ocurrió).\n", - " - Actores clave (quiénes están involucrados).\n", - " * Denunciante(s).\n", - " * Denunciado(s).\n", - " * Testigo(s).\n", - " * Juez(es).\n", - " * Abogado(s).\n", - " * Fiscal(es).\n", - " * Perito(s).\n", - " * Otros (especificar).\n", - " - Cronología (cuándo ocurrieron los hechos y eventos procesales importantes).\n", - " - Lugar(es) (dónde ocurrieron los hechos y eventos procesales importantes).\n", - " - Fundamentos (normas legales, precedentes judiciales, doctrinas aplicadas).\n", - " - Decisión (resultado/medida adoptada).\n", - "\"\"\"\n", - "\n", - "ENTITIES = \"\"\"\n", - " - \"BANCO\": Nombre de una entidad bancaria, pública o privada.\n", - " - \"CBU\": Código Bancario Uniforme (22 dígitos) de una cuenta.\n", - " - \"CORREO_ELECTRONICO\": Dirección de correo electrónico.\n", - " - \"CUIJ\": Código Único de Identificación Jurídica de causas judiciales.\n", - " - \"CUIT_CUIL\": Número de CUIT o CUIL de una persona física o jurídica.\n", - " - \"DIRECCION\": Dirección específica (calle, número, intersección de calles y/o avenidas, código postal, etc.).\n", - " - \"DNI\": Número de Documento Nacional de Identidad u otro documento identificatorio.\n", - " - \"EDAD\": Edad explícita de una persona.\n", - " - \"ESTUDIOS\": Nivel o institución educativa que permita identificar a la persona (ej. \"primario incompleto\", \"secundario completo\", \"Licenciado en…\").\n", - " - \"FECHA\": Fecha completa o parcial (día, mes y/o año).\n", - " - \"LINK\": Enlace o URL a una página web.\n", - " - \"LOC\": Localización geográfica específica (país, provincia, estado, ciudad, localidad, barrio, etc.).\n", - " - \"MARCA_AUTOMOVIL\": Marca de un vehículo (ej. Toyota, Ford, Suzuki, etc.).\n", - " - \"NACIONALIDAD\": Nacionalidad de una persona (ej. \"argentino\", \"brasileña\").\n", - " - \"NUM_ACTUACION\": Número identificatorio de una actuación administrativa o contravencional.\n", - " - \"NUM_CAJA_AHORRO\": Número completo de una caja de ahorro o cuenta bancaria.\n", - " - \"NUM_EXPEDIENTE\": Número de expediente judicial o administrativo.\n", - " - \"NUM_MATRICULA\": Número de matrícula profesional o académica.\n", - " - \"PATENTE_DOMINIO\": Patente o dominio de un vehículo.\n", - " - \"PER\": Nombre y apellido(s) de una persona física. Los nombres inicializados (ej., \"M.T.G\") y los apodos (ej., \"el Gato\") también cuentan como información sensible a anonimizar.\n", - " - \"TELEFONO\": Número telefónico (fijo o celular).\n", - "\"\"\"\n", - "\n", - "\n", - "# Define system prompt\n", - "system_prompt = system_prompts[\"template\"].format(\n", - " information_to_extract=INFORMATION,\n", - " entities_to_identify=ENTITIES,\n", - ")\n", - "\n", - "print(system_prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3efcf14f", - "metadata": {}, - "outputs": [], - "source": [ - "# Get chat response\n", - "chat_response = get_chat_response(system_prompt, document)\n", - "Markdown(chat_response.message.content)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "92f8d337", - "metadata": {}, - "outputs": [], - "source": [ - "def evaluate_chat_response(\n", - " system_prompt: str,\n", - " user_prompt: str,\n", - " model: str = \"llama3.2:3b\",\n", - " options: dict = {\"num_ctx\": 16_384, \"num_predict\": 4096},\n", - ") -> dict:\n", - " \"\"\"\n", - " Get chat response from the model and evaluate tokens and runtime metrics.\n", - "\n", - " Args:\n", - " system_prompt (str): The system prompt.\n", - " user_prompt (str): The prompt from the user.\n", - " model (str, optional): The model to use. Defaults to \"llama3.2:3b\".\n", - " options (dict, optional): The options for the chat.\n", - " Defaults to {\"num_ctx\": 16_384, \"num_predict\": 4096}.\n", - "\n", - " Returns:\n", - " dict: A dictionary containing:\n", - " - response: The content of the response\n", - " - model: The model name\n", - " - input_tokens: Number of tokens in the prompt (prompt_eval_count)\n", - " - input_duration_ms: Time taken to process input tokens in milliseconds\n", - " - output_tokens: Number of tokens in the output (eval_count)\n", - " - output_duration_ms: Time taken to generate output tokens in milliseconds\n", - " - total_tokens: Total number of tokens processed\n", - " - total_duration_ms: Total duration of the request in milliseconds\n", - " - tokens_per_second: Rate of token generation (output tokens / output duration)\n", - " \"\"\"\n", - " start = time.time()\n", - "\n", - " # Get the chat response\n", - " response = get_chat_response(\n", - " user_prompt=user_prompt,\n", - " model=model,\n", - " system_prompt=system_prompt,\n", - " options=options,\n", - " )\n", - "\n", - " end = time.time()\n", - "\n", - " # Extract metrics\n", - " input_tokens = (\n", - " response.prompt_eval_count if hasattr(response, \"prompt_eval_count\") else None\n", - " )\n", - " output_tokens = response.eval_count if hasattr(response, \"eval_count\") else None\n", - " total_tokens = None\n", - "\n", - " # If total_tokens is still None but we have input and output tokens\n", - " if total_tokens is None and input_tokens is not None and output_tokens is not None:\n", - " total_tokens = input_tokens + output_tokens\n", - "\n", - " # Extract durations and convert nanoseconds to milliseconds\n", - " input_duration_ms = None\n", - " if hasattr(response, \"prompt_eval_duration\"):\n", - " input_duration_ms = response.prompt_eval_duration / 1_000_000 # ns to ms\n", - "\n", - " output_duration_ms = None\n", - " if hasattr(response, \"eval_duration\"):\n", - " output_duration_ms = response.eval_duration / 1_000_000 # ns to ms\n", - "\n", - " model_total_duration_ms = None\n", - " if hasattr(response, \"total_duration\"):\n", - " model_total_duration_ms = response.total_duration / 1_000_000 # ns to ms\n", - "\n", - " # Calculate the total duration from our timer (this will include network latency)\n", - " measured_duration_ms = (end - start) * 1000\n", - "\n", - " # Calculate tokens per second for generation\n", - " tokens_per_second = None\n", - " if (\n", - " output_tokens is not None\n", - " and output_duration_ms is not None\n", - " and output_duration_ms > 0\n", - " ):\n", - " tokens_per_second = output_tokens / (output_duration_ms / 1000)\n", - "\n", - " return {\n", - " \"model\": model,\n", - " \"options\": options,\n", - " \"system_prompt\": system_prompt,\n", - " \"user_prompt\": user_prompt,\n", - " \"chat_response\": response.message.content,\n", - " \"input_tokens\": input_tokens,\n", - " \"input_duration_ms\": input_duration_ms,\n", - " \"output_tokens\": output_tokens,\n", - " \"output_duration_ms\": output_duration_ms,\n", - " \"total_tokens\": total_tokens,\n", - " \"model_duration_ms\": model_total_duration_ms,\n", - " \"measured_duration_ms\": measured_duration_ms,\n", - " \"tokens_per_second\": tokens_per_second,\n", - " \"raw_response\": response.model_dump(), # Include the full response object for reference\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5fce8d0f", - "metadata": {}, - "outputs": [], - "source": [ - "# Test our new evaluation function\n", - "test_result = evaluate_chat_response(\n", - " system_prompt=system_prompts[\"task-specific\"],\n", - " user_prompt=document,\n", - ")\n", - "\n", - "# Display the metrics\n", - "print(f\"Response from {test_result['model']}:\")\n", - "print(\"-\" * 50)\n", - "print(test_result[\"raw_response\"])\n", - "print(\"-\" * 50)\n", - "print(f\"Input tokens: {test_result['input_tokens']}\")\n", - "print(f\"Input processing time: {test_result['input_duration_ms']:.2f} ms\")\n", - "print(f\"Output tokens: {test_result['output_tokens']}\")\n", - "print(f\"Output generation time: {test_result['output_duration_ms']:.2f} ms\")\n", - "print(f\"Total tokens: {test_result['total_tokens']}\")\n", - "print(f\"Model reported duration: {test_result['model_duration_ms']:.2f} ms\")\n", - "print(f\"Measured duration: {test_result['measured_duration_ms']:.2f} ms\")\n", - "print(f\"Generation speed: {test_result['tokens_per_second']:.2f} tokens/second\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "68a55804", - "metadata": {}, - "outputs": [], - "source": [ - "errors = []\n", - "results = []" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "868fbd4c", - "metadata": {}, - "outputs": [], - "source": [ - "DEVICE = \"cuda\"\n", - "\n", - "results = load_json(f\"./summarization-benchmark-results-{DEVICE}.json\")\n", - "results[-1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ca81c50", - "metadata": {}, - "outputs": [], - "source": [ - "len(results)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "72c190b3", - "metadata": {}, - "outputs": [], - "source": [ - "for extracted_document in tqdm(\n", - " extracted_documents, total=len(extracted_documents), desc=\"Evaluating documents\"\n", - "):\n", - " doc_path = os.path.basename(extracted_document[\"doc_path\"])\n", - " document = \"\\n\".join(extracted_document[\"detail\"][\"document\"])\n", - "\n", - " if not document.strip():\n", - " print(f\"Skipping empty document: {doc_path}\")\n", - " continue\n", - "\n", - " for model in MODELS:\n", - " for system_prompt_type in system_prompts:\n", - " print(\n", - " f\"Evaluating {doc_path} with model {model} and prompt {system_prompt_type}\"\n", - " )\n", - " if any(\n", - " r\n", - " for r in results\n", - " if r[\"doc_path\"] == doc_path\n", - " and r[\"model\"] == model\n", - " and r[\"system_prompt_type\"] == system_prompt_type\n", - " and r[\"device\"] == DEVICE\n", - " ):\n", - " print(\n", - " f\"Skipping {doc_path} with model {model} and prompt {system_prompt_type} on {DEVICE} (already evaluated)\"\n", - " )\n", - " continue\n", - "\n", - " system_prompt = system_prompts[system_prompt_type]\n", - " if system_prompt_type == \"template\":\n", - " system_prompt = system_prompts[\"template\"].format(\n", - " information_to_extract=INFORMATION,\n", - " entities_to_identify=ENTITIES,\n", - " )\n", - "\n", - " try:\n", - " result = evaluate_chat_response(\n", - " system_prompt=system_prompt,\n", - " user_prompt=document,\n", - " model=model,\n", - " )\n", - " result[\"doc_path\"] = doc_path\n", - " result[\"system_prompt_type\"] = system_prompt_type\n", - " result[\"device\"] = DEVICE\n", - " results.append(result)\n", - "\n", - " save_json(\n", - " results,\n", - " f\"./summarization-benchmark-results-{DEVICE}.json\",\n", - " )\n", - "\n", - " except Exception as e:\n", - " print(f\"Error evaluating {doc_path} with model {model}: {e}\")\n", - " errors.append(\n", - " {\n", - " \"doc_path\": doc_path,\n", - " \"model\": model,\n", - " \"system_prompt\": system_prompt,\n", - " \"device\": DEVICE,\n", - " \"error\": str(e),\n", - " }\n", - " )\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1359dec5", - "metadata": {}, - "outputs": [], - "source": [ - "len(errors)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "849850e9", - "metadata": {}, - "outputs": [], - "source": [ - "errors" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cc9c3cab", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai", - "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.10.18" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/summarization/02-statistical-analysis.ipynb b/notebooks/experiments/summarization/02-statistical-analysis.ipynb deleted file mode 100644 index aef5a7d9..00000000 --- a/notebooks/experiments/summarization/02-statistical-analysis.ipynb +++ /dev/null @@ -1,1651 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "f5458e0a", - "metadata": {}, - "source": [ - "# Summaries Analysis\n", - "\n", - "This notebook performs both **quantitative** and **qualitative** analyses of the summaries generated by the API, to build a complete picture of model summary performance.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "481c1d32", - "metadata": {}, - "source": [ - "\n", - "\n", - "**1. Quantitative Analysis**\n", - "- Conduct a **statistical and exploratory data analysis** of the API results using Pandas and descriptive statistics. \n", - "- Explore and refine the **visualizations** to better highlight differences and distributions. \n", - "- Compare **model runtimes** (CPU vs GPU) for the same document to assess performance efficiency. \n", - "\n", - "\n", - "\n", - "**1. Qualitative Analysis**\n", - "- **Garbage Detection:** Identify low-quality or nonsensical summaries (e.g., hallucinations or “garbage” outputs). \n", - " These can often be detected as **outliers** in the quantitative performance metrics. \n", - " DeepSeek has shown clear examples of this behavior. \n", - "- **Summary Comparison:** Evaluate the **relative quality and performance** of summaries across different models, linking the findings with the quantitative metrics.\n" - ] - }, - { - "cell_type": "markdown", - "id": "69039406", - "metadata": {}, - "source": [ - "## Quantitative Analysis" - ] - }, - { - "cell_type": "markdown", - "id": "d98633a6", - "metadata": {}, - "source": [ - "#### Load data and function definitions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5be98b6f", - "metadata": {}, - "outputs": [], - "source": [ - "# Libraries imports\n", - "\n", - "import os\n", - "import json\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns" - ] - }, - { - "cell_type": "markdown", - "id": "b4b981af", - "metadata": {}, - "source": [ - "Function definitions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d58de8bc", - "metadata": {}, - "outputs": [], - "source": [ - "# Attempt to identify parameter scale (in billions) from model names\n", - "def get_model_parameter_scale(model_name):\n", - " import re\n", - "\n", - " if not model_name:\n", - " return None\n", - "\n", - " known_params = {\n", - " \"gemma3:12b\": 12,\n", - " \"gemma3:40b\": 40,\n", - " \"gemma3:20b\": 20,\n", - " \"gemma3:22b\": 22,\n", - " \"gemma3:38b\": 38,\n", - " \"llama3.1:8b\": 8,\n", - " \"llama3.2:3b\": 3,\n", - " \"llama3.2:11b\": 11,\n", - " \"deepseek-r1:1.1b\": 1.1,\n", - " \"deepseek-r1:1b\": 1,\n", - " \"phi-3:3.8b\": 3.8,\n", - " \"phi-4:14b\": 14,\n", - " \"qwen3:3.8b\": 3.8,\n", - " \"qwen3:14b\": 14,\n", - " \"qwen3:72b\": 72,\n", - " }\n", - "\n", - " model_name_lower = model_name.lower()\n", - " if model_name_lower in known_params:\n", - " return known_params[model_name_lower]\n", - "\n", - " match = re.search(r\"(\\d+(?:\\.\\d+)?)\\s*b\", model_name_lower)\n", - " if match:\n", - " try:\n", - " return float(match.group(1))\n", - " except ValueError:\n", - " return None\n", - " return None\n", - "\n", - "\n", - "def unique_vals(df, col):\n", - " print(col)\n", - " return print(f\"Total {col}: {(df[col].nunique())} \\n {(df[col].unique())} \\n\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "03ae93cc", - "metadata": {}, - "outputs": [], - "source": [ - "DATA_PATH = \"/resources/data/restricted/summarization\" # Change this to your data path\n", - "cuda_summaries_file = os.path.join(\n", - " DATA_PATH, \"summarization-benchmark-results-cuda.json\"\n", - ")\n", - "cpu_summaries_file = os.path.join(DATA_PATH, \"summarization-benchmark-results-cpu.json\")\n", - "\n", - "with open(cuda_summaries_file, \"r\") as f:\n", - " cuda_summaries = json.load(f)\n", - "with open(cpu_summaries_file, \"r\") as f:\n", - " cpu_summaries = json.load(f)\n", - "\n", - "summaries = cuda_summaries + cpu_summaries\n", - "df = pd.DataFrame(summaries)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64887e36", - "metadata": {}, - "outputs": [], - "source": [ - "df.doc_path.nunique()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9eff2b81", - "metadata": {}, - "outputs": [], - "source": [ - "df.doc_path = df.doc_path.apply(lambda x: os.path.basename(x))\n", - "df.doc_path.nunique()" - ] - }, - { - "cell_type": "markdown", - "id": "9000571b", - "metadata": {}, - "source": [ - "### Descriptive analysis" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4081f62e", - "metadata": {}, - "outputs": [], - "source": [ - "df.head(4)" - ] - }, - { - "cell_type": "markdown", - "id": "bdaddf02", - "metadata": {}, - "source": [ - "#### Prompts visualization/print" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3dd1c488", - "metadata": {}, - "outputs": [], - "source": [ - "import pprint\n", - "\n", - "unique = (\n", - " df[[\"system_prompt\", \"system_prompt_type\"]].drop_duplicates().reset_index(drop=True)\n", - ")\n", - "\n", - "for _, row in unique.iterrows():\n", - " print(\"Type:\", row[\"system_prompt_type\"])\n", - " print(\"Prompt:\")\n", - " pprint.pprint(row[\"system_prompt\"])\n", - " print(\"---\\n\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f7a99302", - "metadata": {}, - "outputs": [], - "source": [ - "df.info()" - ] - }, - { - "cell_type": "markdown", - "id": "9b7300d5", - "metadata": {}, - "source": [ - "#### Data engineer (ms -> mins)" - ] - }, - { - "cell_type": "markdown", - "id": "f628dc9e", - "metadata": {}, - "source": [ - "Create new columns to mins unit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2ed9c4c4", - "metadata": {}, - "outputs": [], - "source": [ - "ms_cols = [c for c in df.columns if c.endswith(\"ms\")]\n", - "ms_min_cols = [[c.replace(\"ms\", \"min\"), c] for c in ms_cols]\n", - "time_cols = [c[0] for c in ms_min_cols]\n", - "\n", - "for cols in ms_min_cols:\n", - " c, ms_col = cols\n", - " df[c] = df[ms_col] / 1000 / 60\n", - "\n", - "df.head(2)\n", - "\n", - "time_cols" - ] - }, - { - "cell_type": "markdown", - "id": "4d25024d", - "metadata": {}, - "source": [ - "#### Describe columns" - ] - }, - { - "cell_type": "markdown", - "id": "33014b1c", - "metadata": {}, - "source": [ - "We describe the float type columns:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "61cc141c", - "metadata": {}, - "outputs": [], - "source": [ - "desc = (\n", - " df[\n", - " [\n", - " \"input_tokens\",\n", - " \"output_tokens\",\n", - " \"total_tokens\",\n", - " \"input_duration_min\",\n", - " \"output_duration_min\",\n", - " \"model_duration_min\",\n", - " \"tokens_per_second\",\n", - " ]\n", - " ]\n", - " .describe()\n", - " .T\n", - ")\n", - "print(desc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6dbb2ba5", - "metadata": {}, - "outputs": [], - "source": [ - "840 / 60" - ] - }, - { - "cell_type": "markdown", - "id": "116603ca", - "metadata": {}, - "source": [ - "We observed that the 75% of the results have model_duration_min <= 7.7 minutes, and the is at list one outlier with model_duration_min = 840 minutes (14 hours)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bcd5ccbd", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"model_duration_min\"] > 840]" - ] - }, - { - "cell_type": "markdown", - "id": "7945a14f", - "metadata": {}, - "source": [ - "This is an outlier due the garbage of its output:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f02adf9b", - "metadata": {}, - "outputs": [], - "source": [ - "import pprint\n", - "\n", - "df[df[\"model_duration_min\"] > 840].chat_response.iloc[0][-200:]" - ] - }, - { - "cell_type": "markdown", - "id": "1ddcf0b0", - "metadata": {}, - "source": [ - "Visualization of unique values per main columns:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e0ec9e75", - "metadata": {}, - "outputs": [], - "source": [ - "for col in [\n", - " \"model\",\n", - " \"system_prompt\",\n", - " \"device\",\n", - " \"system_prompt_type\",\n", - " \"user_prompt\",\n", - " \"doc_path\",\n", - "]:\n", - " unique_vals(df, col)" - ] - }, - { - "cell_type": "markdown", - "id": "2e1f550d", - "metadata": {}, - "source": [ - "### Model comparison" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3d684ef", - "metadata": {}, - "outputs": [], - "source": [ - "df.groupby(\"model\")[\n", - " [\"tokens_per_second\", \"model_duration_min\", \"total_tokens\"]\n", - "].mean().sort_values(\"tokens_per_second\", ascending=False)\n", - "df.groupby([\"model\", \"device\"])[[\"model_duration_min\", \"tokens_per_second\"]].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4f749026", - "metadata": {}, - "outputs": [], - "source": [ - "# Runtime distribution\n", - "plt.figure(figsize=(8, 5), dpi=50)\n", - "sns.boxplot(data=df, x=\"model\", y=\"model_duration_min\", hue=\"device\")\n", - "plt.xticks(rotation=45)\n", - "plt.title(\"Model Duration by Device\")\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "86d0a9a8", - "metadata": {}, - "source": [ - "Since deepseek-r1:8b has a duration one or two orders of magnitude longer compared to other models, we removed it from the visualization to avoid scaling issues:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "646c45d5", - "metadata": {}, - "outputs": [], - "source": [ - "df_to_plot = df[df[\"model\"] != \"deepseek-r1:8b\"]\n", - "# Runtime distribution\n", - "plt.figure(figsize=(8, 5), dpi=100)\n", - "sns.boxplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"device\")\n", - "plt.xticks(rotation=45)\n", - "plt.title(\"Tokens per second by device\")\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2ceada36", - "metadata": {}, - "outputs": [], - "source": [ - "df_to_plot = df[df[\"model\"] != \"deepseek-r1:8b\"]\n", - "# Runtime distribution\n", - "plt.figure(figsize=(8, 5), dpi=100)\n", - "sns.boxplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"system_prompt_type\")\n", - "plt.xticks(rotation=45)\n", - "plt.title(\"Model Duration by device\")\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "30d74a25", - "metadata": {}, - "outputs": [], - "source": [ - "df_to_plot = df[df[\"model\"] != \"deepseek-r1:8b\"]\n", - "# Runtime distribution\n", - "plt.figure(figsize=(8, 5), dpi=100)\n", - "sns.boxplot(\n", - " data=df_to_plot, x=\"model\", y=\"model_duration_min\", hue=\"system_prompt_type\"\n", - ")\n", - "plt.xticks(rotation=45)\n", - "plt.title(\"Model Duration by device\")\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "3b6df54f", - "metadata": {}, - "source": [ - "It can be seen how much better cuda performs over cpu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8fd45e41", - "metadata": {}, - "outputs": [], - "source": [ - "# Throughput\n", - "plt.figure(figsize=(8, 5), dpi=100)\n", - "sns.barplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"system_prompt_type\")\n", - "plt.xticks(rotation=45)\n", - "plt.grid(True)\n", - "plt.title(\"Throughput (tokens/sec) by Model and Prompt Type\")\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60c2cbb3", - "metadata": {}, - "outputs": [], - "source": [ - "# Throughput\n", - "plt.figure(figsize=(8, 5), dpi=100)\n", - "sns.barplot(\n", - " data=df_to_plot, x=\"model\", y=\"model_duration_min\", hue=\"system_prompt_type\"\n", - ")\n", - "plt.xticks(rotation=45)\n", - "plt.grid(True)\n", - "plt.ylabel(\"Model duration (min)\")\n", - "plt.title(\"Tokens per second by Model and Prompt Type\")\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a8c681b9", - "metadata": {}, - "outputs": [], - "source": [ - "# Token ratios\n", - "plt.figure(figsize=(8, 5), dpi=100)\n", - "df_to_plot[\"compression_ratio\"] = df_to_plot[\"output_tokens\"] / df[\"input_tokens\"]\n", - "sns.violinplot(data=df_to_plot, x=\"model\", y=\"compression_ratio\")\n", - "plt.xticks(rotation=45)\n", - "plt.grid(True)\n", - "plt.title(\"Output/Input Token Ratio (Compression Quality Proxy)\")\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "8bc2cfc2", - "metadata": {}, - "source": [ - "A nivel " - ] - }, - { - "cell_type": "markdown", - "id": "36a2746e", - "metadata": {}, - "source": [ - "The mean imput tokens by model are:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb3d4078", - "metadata": {}, - "outputs": [], - "source": [ - "for filter, df_filtered in [\n", - " (\"all\", df),\n", - " (\"cuda\", df[df[\"device\"] == \"cuda\"]),\n", - " (\"cpu\", df[df[\"device\"] == \"cpu\"]),\n", - "]:\n", - " fig, axes = plt.subplots(ncols=2, figsize=(10, 5))\n", - " label = filter if filter != \"all\" else filter + \" devices\"\n", - "\n", - " sns.barplot(\n", - " data=df_filtered,\n", - " x=\"model\",\n", - " y=\"tokens_per_second\",\n", - " ax=axes[0],\n", - " order=vc.index if \"vc\" in globals() else None,\n", - " label=label,\n", - " )\n", - " axes[0].set_title(\"Generation Speed by Model\")\n", - " axes[0].set_ylabel(\"Tokens per Second\")\n", - " axes[0].tick_params(axis=\"x\", rotation=45)\n", - " axes[0].grid(axis=\"y\", linestyle=\"--\", alpha=0.7)\n", - "\n", - " sns.barplot(\n", - " data=df_filtered,\n", - " x=\"model\",\n", - " y=\"measured_duration_min\",\n", - " ax=axes[1],\n", - " order=vc.index if \"vc\" in globals() else None,\n", - " label=label,\n", - " )\n", - " axes[1].set_title(\"Total Processing Time by Model\")\n", - " axes[1].set_ylabel(\"Duration (min)\")\n", - " axes[1].tick_params(axis=\"x\", rotation=45)\n", - " axes[1].grid(axis=\"y\", linestyle=\"--\", alpha=0.7)\n", - "\n", - " plt.tight_layout()\n", - " plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "a7afc1f2", - "metadata": {}, - "source": [ - "### Gap Analysis (CPU - CUDA)" - ] - }, - { - "cell_type": "markdown", - "id": "ea239bb0", - "metadata": {}, - "source": [ - "#### Time Gap" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7218eea4", - "metadata": {}, - "outputs": [], - "source": [ - "# time gap between CPU and CUDA (cpu_ms - cuda_ms)\n", - "pivot = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", - "pivot.head()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "50c879eb", - "metadata": {}, - "outputs": [], - "source": [ - "# time gap between CPU and CUDA (cpu_ms - cuda_ms)\n", - "pivot = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", - "if {\"cpu\", \"cuda\"}.issubset(pivot.columns):\n", - " # ensure no missing values and work on a copy\n", - " pivot = pivot.reindex(pivot.index).fillna(0).copy()\n", - "\n", - " # reset index so 'model' becomes a column\n", - " pivot = pivot.reset_index().rename(columns={\"index\": \"model\"})\n", - "\n", - " # ensure numeric types and compute difference (values are already in minutes)\n", - " pivot[\"cpu\"] = pd.to_numeric(pivot[\"cpu\"], errors=\"coerce\").fillna(0)\n", - " pivot[\"cuda\"] = pd.to_numeric(pivot[\"cuda\"], errors=\"coerce\").fillna(0)\n", - " pivot[\"cpu_minus_cuda_min\"] = pivot[\"cpu\"] - pivot[\"cuda\"]\n", - "\n", - " # build a 1-D list of model names ordered by the gap\n", - " order = list(pivot.sort_values(\"cpu_minus_cuda_min\", ascending=False)[\"model\"])\n", - "\n", - " plt.figure(figsize=(12, 5))\n", - " sns.barplot(data=pivot, x=\"model\", y=\"cpu_minus_cuda_min\", order=order)\n", - " plt.axhline(0, color=\"k\", linestyle=\"--\", linewidth=0.8)\n", - " plt.xticks(rotation=45)\n", - " plt.ylabel(\"CPU - CUDA duration (min)\")\n", - " plt.title(\"Time gap between CPU and CUDA by model (CPU - CUDA)\")\n", - " plt.tight_layout()\n", - " plt.show()\n", - "else:\n", - " print(\n", - " \"Not enough data to compute CPU vs CUDA gap (missing 'cpu' or 'cuda' device rows).\"\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "583b4638", - "metadata": {}, - "source": [ - "#### Token per second gap" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0ea8df17", - "metadata": {}, - "outputs": [], - "source": [ - "# time gap between CPU and CUDA (cpu_ms - cuda_ms)\n", - "pivot = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", - "if {\"cpu\", \"cuda\"}.issubset(pivot.columns):\n", - " # ensure no missing values and work on a copy\n", - " pivot = pivot.reindex(pivot.index).fillna(0).copy()\n", - "\n", - " # reset index so 'model' becomes a column\n", - " pivot = pivot.reset_index().rename(columns={\"index\": \"model\"})\n", - "\n", - " # ensure numeric types and compute difference (values are already in minutes)\n", - " pivot[\"cpu\"] = pd.to_numeric(pivot[\"cpu\"], errors=\"coerce\").fillna(0)\n", - " pivot[\"cuda\"] = pd.to_numeric(pivot[\"cuda\"], errors=\"coerce\").fillna(0)\n", - " pivot[\"cpu_minus_cuda_min\"] = pivot[\"cpu\"] - pivot[\"cuda\"]\n", - "\n", - " # build a 1-D list of model names ordered by the gap\n", - " order = list(pivot.sort_values(\"cpu_minus_cuda_min\", ascending=False)[\"model\"])\n", - "\n", - " plt.figure(figsize=(12, 5))\n", - " sns.barplot(data=pivot, x=\"model\", y=\"cpu_minus_cuda_min\", order=order)\n", - " plt.axhline(0, color=\"k\", linestyle=\"--\", linewidth=0.8)\n", - " plt.xticks(rotation=45)\n", - " plt.ylabel(\"CPU - CUDA tokens per second gap\")\n", - " plt.title(\"Token per second gap between CPU and CUDA by model (CPU - CUDA)\")\n", - " plt.tight_layout()\n", - " plt.show()\n", - "else:\n", - " print(\n", - " \"Not enough data to compute CPU vs CUDA gap (missing 'cpu' or 'cuda' device rows).\"\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bebfb4d2", - "metadata": {}, - "outputs": [], - "source": [ - "df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "797cfad4", - "metadata": {}, - "outputs": [], - "source": [ - "pivot_token = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", - "pivot_dur = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", - "\n", - "# prepare a list of models to keep consistent ordering (try to reuse vc order if present)\n", - "models = list(vc.index) if \"vc\" in globals() else sorted(df[\"model\"].unique())\n", - "\n", - "\n", - "# build a safe summary even if one device is missing\n", - "def safe_gap(pivot, models):\n", - " # ensure index contains all models and missing values filled with 0\n", - " p = (\n", - " pivot.reindex(models)\n", - " .fillna(0)\n", - " .copy()\n", - " .reset_index()\n", - " .rename(columns={\"index\": \"model\"})\n", - " )\n", - " # coerce numeric and add cpu/cuda columns if missing\n", - " p[\"cpu\"] = pd.to_numeric(p.get(\"cpu\", 0), errors=\"coerce\").fillna(0)\n", - " p[\"cuda\"] = pd.to_numeric(p.get(\"cuda\", 0), errors=\"coerce\").fillna(0)\n", - " return p\n", - "\n", - "\n", - "p_token = safe_gap(pivot_token, models)\n", - "p_dur = safe_gap(pivot_dur, models)\n", - "\n", - "model_summary = pd.DataFrame(\n", - " {\n", - " \"model\": p_token[\"model\"],\n", - " \"gap_input_tokens\": p_token[\"cpu\"] - p_token[\"cuda\"],\n", - " # convert minutes gap to seconds\n", - " \"gap_total_duration_min\": (p_dur[\"cpu\"] - p_dur[\"cuda\"]),\n", - " }\n", - ")\n", - "\n", - "# bubble size: proportional to number of samples per model (scaled for plotting)\n", - "counts = df.groupby(\"model\").size().reindex(model_summary[\"model\"]).fillna(1)\n", - "model_summary[\"bubble_size\"] = (counts / counts.max()) * 400 + 50\n", - "\n", - "# ensure columns have expected dtypes\n", - "model_summary[\"gap_input_tokens\"] = pd.to_numeric(\n", - " model_summary[\"gap_input_tokens\"], errors=\"coerce\"\n", - ").fillna(0)\n", - "model_summary[\"gap_total_duration_min\"] = pd.to_numeric(\n", - " model_summary[\"gap_total_duration_min\"], errors=\"coerce\"\n", - ").fillna(0)\n", - "\n", - "model_summary.head()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c00d6e92", - "metadata": {}, - "outputs": [], - "source": [ - "pivot_token = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", - "pivot_dur = df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", - "\n", - "# prepare a list of models to keep consistent ordering (try to reuse vc order if present)\n", - "models = list(vc.index) if \"vc\" in globals() else sorted(df[\"model\"].unique())\n", - "\n", - "\n", - "# build a safe summary even if one device is missing\n", - "def safe_gap(pivot, models):\n", - " # ensure index contains all models and missing values filled with 0\n", - " p = (\n", - " pivot.reindex(models)\n", - " .fillna(0)\n", - " .copy()\n", - " .reset_index()\n", - " .rename(columns={\"index\": \"model\"})\n", - " )\n", - " # coerce numeric and add cpu/cuda columns if missing\n", - " p[\"cpu\"] = pd.to_numeric(p.get(\"cpu\", 0), errors=\"coerce\").fillna(0)\n", - " p[\"cuda\"] = pd.to_numeric(p.get(\"cuda\", 0), errors=\"coerce\").fillna(0)\n", - " return p\n", - "\n", - "\n", - "p_token = safe_gap(pivot_token, models)\n", - "p_dur = safe_gap(pivot_dur, models)\n", - "\n", - "model_summary = pd.DataFrame(\n", - " {\n", - " \"model\": p_token[\"model\"],\n", - " \"gap_tokens\": p_token[\"cpu\"] - p_token[\"cuda\"],\n", - " # convert minutes gap to seconds\n", - " \"gap_total_duration_min\": (p_dur[\"cpu\"] - p_dur[\"cuda\"]),\n", - " }\n", - ")\n", - "\n", - "# bubble size: proportional to number of samples per model (scaled for plotting)\n", - "counts = df.groupby(\"model\").size().reindex(model_summary[\"model\"]).fillna(1)\n", - "model_summary[\"bubble_size\"] = (counts / counts.max()) * 400 + 50\n", - "\n", - "# ensure columns have expected dtypes\n", - "model_summary[\"gap_tokens\"] = pd.to_numeric(\n", - " model_summary[\"gap_tokens\"], errors=\"coerce\"\n", - ").fillna(0)\n", - "model_summary[\"gap_total_duration_min\"] = pd.to_numeric(\n", - " model_summary[\"gap_total_duration_min\"], errors=\"coerce\"\n", - ").fillna(0)\n", - "\n", - "model_summary.head()\n", - "\n", - "\n", - "fig, ax = plt.subplots(figsize=(12, 6))\n", - "ax.scatter(\n", - " model_summary[\"gap_tokens\"],\n", - " model_summary[\"gap_total_duration_min\"],\n", - " # s=model_summary[\"bubble_size\"],\n", - " alpha=0.7,\n", - " color=\"#1f77b4\",\n", - " edgecolors=\"black\",\n", - ")\n", - "for _, row in model_summary.iterrows():\n", - " ax.text(\n", - " row[\"gap_tokens\"],\n", - " row[\"gap_total_duration_min\"],\n", - " row[\"model\"],\n", - " fontsize=9,\n", - " ha=\"left\",\n", - " va=\"bottom\",\n", - " alpha=0.9,\n", - " )\n", - "ax.set_ylabel(\"Average Total Duration Gap (min)\")\n", - "ax.set_xlabel(\"Average Tokens per second\")\n", - "# ax.set_ylabel(\"Average Total Duration (s)\")\n", - "ax.grid(True, linestyle=\"--\", alpha=0.6)\n", - "ax.set_title(\"CPU vs CUDA Performance Gap by Model\")\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8542204f", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7c42e753", - "metadata": {}, - "outputs": [], - "source": [ - "def make_summary(df):\n", - " pivot_token = df.groupby([\"model\", \"device\"])[\"tokens_per_second\"].mean().unstack()\n", - " pivot_dur = (\n", - " df.groupby([\"model\", \"device\"])[\"measured_duration_min\"].mean().unstack()\n", - " )\n", - "\n", - " # prepare a list of models to keep consistent ordering\n", - " models = sorted(df[\"model\"].unique())\n", - "\n", - " # build a safe summary even if one device is missing\n", - " def safe_gap(pivot, models):\n", - " p = (\n", - " pivot.reindex(models)\n", - " .fillna(0)\n", - " .copy()\n", - " .reset_index()\n", - " .rename(columns={\"index\": \"model\"})\n", - " )\n", - " p[\"cpu\"] = pd.to_numeric(p.get(\"cpu\", 0), errors=\"coerce\").fillna(0)\n", - " p[\"cuda\"] = pd.to_numeric(p.get(\"cuda\", 0), errors=\"coerce\").fillna(0)\n", - " return p\n", - "\n", - " p_token = safe_gap(pivot_token, models)\n", - " p_dur = safe_gap(pivot_dur, models)\n", - "\n", - " model_summary = pd.DataFrame(\n", - " {\n", - " \"model\": p_token[\"model\"],\n", - " # CUDA - CPU gap for tokens per second\n", - " \"gap_tokens\": p_token[\"cuda\"] - p_token[\"cpu\"],\n", - " # CUDA - CPU gap for total duration (minutes)\n", - " \"gap_total_duration_min\": p_dur[\"cuda\"] - p_dur[\"cpu\"],\n", - " }\n", - " )\n", - "\n", - " # add parameter scale and bubble size\n", - " model_summary[\"param_scale\"] = model_summary[\"model\"].apply(\n", - " get_model_parameter_scale\n", - " )\n", - " model_summary[\"param_scale\"] = pd.to_numeric(\n", - " model_summary[\"param_scale\"], errors=\"coerce\"\n", - " ).fillna(0)\n", - "\n", - " # counts = df.groupby('model').size().reindex(model_summary['model']).fillna(1)\n", - " # model_summary['bubble_size'] = (counts / counts.max()) * 400 + 50\n", - "\n", - " # ensure numeric types and no NaNs\n", - " model_summary[\"gap_tokens\"] = pd.to_numeric(\n", - " model_summary[\"gap_tokens\"], errors=\"coerce\"\n", - " ).fillna(0)\n", - " model_summary[\"gap_total_duration_min\"] = pd.to_numeric(\n", - " model_summary[\"gap_total_duration_min\"], errors=\"coerce\"\n", - " ).fillna(0)\n", - "\n", - " return model_summary\n", - "\n", - "\n", - "model_summary = make_summary(df)\n", - "model_summary.head()\n", - "\n", - "model_summary.head()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9fef1bba", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "\n", - "# generate a unique color per model\n", - "palette = sns.color_palette(n_colors=len(model_summary))\n", - "model_summary.sort_values(\"param_scale\", inplace=True)\n", - "model_colors = dict(zip(model_summary[\"model\"], palette))\n", - "\n", - "fig, ax = plt.subplots(figsize=(12, 6))\n", - "i = 1\n", - "# plot bubbles with model-specific colors\n", - "for _, row in model_summary.iterrows():\n", - " color = model_colors[row[\"model\"]]\n", - " ax.scatter(\n", - " row[\"param_scale\"], # row[\"gap_total_duration_min\"],\n", - " row[\"gap_tokens\"],\n", - " s=row[\"param_scale\"] * 100,\n", - " alpha=0.7,\n", - " color=color,\n", - " edgecolors=\"black\",\n", - " linewidth=0.7,\n", - " )\n", - " ax.text(\n", - " row[\"param_scale\"] + (0.5 * (-1) ** i), # row[\"gap_total_duration_min\"]\n", - " row[\"gap_tokens\"],\n", - " row[\"model\"],\n", - " fontsize=9,\n", - " ha=\"left\",\n", - " va=\"bottom\",\n", - " alpha=1,\n", - " fontweight=\"bold\",\n", - " color=color,\n", - " )\n", - " i += 1\n", - "\n", - " # add a label for the bubble size\n", - "\n", - "ax.set_ylabel(\"Average Tokens per Second\")\n", - "ax.set_xlabel(\"Parameter Scale (Billion Parameters)\")\n", - "ax.grid(True, linestyle=\"--\", alpha=0.6)\n", - "ax.set_title(\"CUDA - CPU Gap Token per Second by Model Parameter Scale per Model\")\n", - "\n", - "# optional legend showing color per model\n", - "handles = [\n", - " plt.Line2D(\n", - " [0],\n", - " [0],\n", - " marker=\"o\",\n", - " color=\"w\",\n", - " label=model,\n", - " markerfacecolor=color,\n", - " markersize=8,\n", - " markeredgecolor=\"black\",\n", - " )\n", - " for model, color in model_colors.items()\n", - "]\n", - "ax.legend(handles=handles, title=\"Model\", loc=\"best\")\n", - "\n", - "plt.tight_layout()\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cdfdde71", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "\n", - "# generate a unique color per model\n", - "palette = sns.color_palette(n_colors=len(model_summary))\n", - "model_summary.sort_values(\"param_scale\", inplace=True)\n", - "model_colors = dict(zip(model_summary[\"model\"], palette))\n", - "\n", - "fig, ax = plt.subplots(figsize=(12, 6), dpi=100)\n", - "i = 1\n", - "# plot bubbles with model-specific colors\n", - "for _, row in model_summary.iterrows():\n", - " color = model_colors[row[\"model\"]]\n", - " ax.scatter(\n", - " row[\"param_scale\"], # row[\"gap_total_duration_min\"],\n", - " row[\"gap_total_duration_min\"],\n", - " s=row[\"param_scale\"] * 100,\n", - " alpha=0.7,\n", - " color=color,\n", - " edgecolors=\"black\",\n", - " linewidth=0.7,\n", - " )\n", - " ax.text(\n", - " row[\"param_scale\"] + (0.5 * (-1) ** i), # row[\"gap_total_duration_min\"]\n", - " row[\"gap_total_duration_min\"],\n", - " row[\"model\"],\n", - " fontsize=9,\n", - " ha=\"left\",\n", - " va=\"bottom\",\n", - " alpha=1,\n", - " fontweight=\"bold\",\n", - " color=color,\n", - " )\n", - " i += 1\n", - "\n", - " # add a label for the bubble size\n", - "\n", - "ax.set_ylabel(\"Average Total Duration Gap (min)\")\n", - "ax.set_xlabel(\"Parameter Scale (Billion Parameters)\")\n", - "ax.grid(True, linestyle=\"--\", alpha=0.6)\n", - "ax.set_title(\"CUDA - CPU Gap Token per Second by Model Parameter Scale per Model\")\n", - "\n", - "# optional legend showing color per model\n", - "handles = [\n", - " plt.Line2D(\n", - " [0],\n", - " [0],\n", - " marker=\"o\",\n", - " color=\"w\",\n", - " label=model,\n", - " markerfacecolor=color,\n", - " markersize=8,\n", - " markeredgecolor=\"black\",\n", - " )\n", - " for model, color in model_colors.items()\n", - "]\n", - "ax.legend(handles=handles, title=\"Model\", loc=\"best\")\n", - "\n", - "plt.tight_layout()\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "36d4c49b", - "metadata": {}, - "outputs": [], - "source": [ - "(\n", - " df.iloc[0].output_tokens / df.iloc[0].output_duration_ms\n", - ") * 1000 # it is tokens_per_second" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8666d95d", - "metadata": {}, - "outputs": [], - "source": [ - "from aymurai.utils.json_data import load_json, save_json\n", - "\n", - "save_json(\n", - " summaries,\n", - " DATA_PATH + \"summaries.json\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eae7c0e6", - "metadata": {}, - "outputs": [], - "source": [ - "df.iloc[0].input_duration_ms + df.iloc[0].output_duration_ms # total duration in ms" - ] - }, - { - "cell_type": "markdown", - "id": "8d4847a7", - "metadata": {}, - "source": [ - "## Qualitative Analysis" - ] - }, - { - "cell_type": "markdown", - "id": "4c19dacc", - "metadata": {}, - "source": [ - "### Garbage Detection - Outliers " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0bba4ac3", - "metadata": {}, - "outputs": [], - "source": [ - "df.describe()" - ] - }, - { - "cell_type": "markdown", - "id": "600d3507", - "metadata": {}, - "source": [ - "max output_tokens is 163840, same value as max input token, so, it is truncated. We will see examples" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8cfce3c0", - "metadata": {}, - "outputs": [], - "source": [ - "df_truncated = df[df[\"output_tokens\"] > 16000]\n", - "print(\"Truncated summary analysis: \\ndoc paths: \\n\", df_truncated.doc_path.unique())\n", - "print(\"Models: \\n\", df_truncated.model.unique())\n", - "df_truncated.head()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77a9fe88", - "metadata": {}, - "outputs": [], - "source": [ - "df_truncated.groupby([\"model\", \"doc_path\", \"system_prompt_type\"]).count()" - ] - }, - { - "cell_type": "markdown", - "id": "fc67ba29", - "metadata": {}, - "source": [ - "11 of 12 outputs truncated correspond to deepseek-r1:8b model, and the other one to phi3:3.8b. We will see the hallucinations bellow" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "687db5cd", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 6))\n", - "sns.boxplot(data=df, x=\"model\", y=\"model_duration_ms\")\n", - "plt.xticks(rotation=45)\n", - "plt.title(\"Runtime Variability Across Documents\")\n", - "plt.ylabel(\"Model Duration (ms)\")\n", - "plt.grid(True, alpha=0.4)\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fba9021e", - "metadata": {}, - "outputs": [], - "source": [ - "corr = df_to_plot[\n", - " [\n", - " \"input_tokens\",\n", - " \"output_tokens\",\n", - " \"model_duration_ms\",\n", - " \"tokens_per_second\",\n", - " \"compression_ratio\",\n", - " ]\n", - "].corr()\n", - "sns.heatmap(corr, annot=True, cmap=\"coolwarm\", center=0)\n", - "plt.title(\"Correlation Between Metrics\")\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "95666d38", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 6))\n", - "sns.boxplot(data=df_to_plot, x=\"model\", y=\"tokens_per_second\", hue=\"device\")\n", - "plt.xticks(rotation=45)\n", - "plt.title(\"Tokens per Second Variability Across Documents\")\n", - "plt.ylabel(\"Tokens per Second\")\n", - "plt.grid(True, alpha=0.4)\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dce57b6a", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 6))\n", - "sns.boxplot(data=df_to_plot, x=\"model\", y=\"model_duration_min\")\n", - "plt.xticks(rotation=45)\n", - "plt.title(\"Runtime Variability Across Documents\")\n", - "plt.ylabel(\"Model Duration (min)\")\n", - "plt.grid(True, alpha=0.4)\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "19b0ed7e", - "metadata": {}, - "source": [ - "### Analysis by document" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a4f0af7", - "metadata": {}, - "outputs": [], - "source": [ - "df_cuda = df[df[\"device\"] == \"cuda\"]\n", - "plt.figure(figsize=(10, 6))\n", - "sns.boxplot(data=df_cuda, x=\"doc_path\", y=\"tokens_per_second\", hue=\"model\")\n", - "plt.xticks(rotation=90)\n", - "plt.title(\"Tokens per second by Document and Model - CUDA\")\n", - "plt.grid(True)\n", - "plt.tight_layout()\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8c4f88ac", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(10, 6))\n", - "sns.boxplot(data=df, x=\"doc_path\", y=\"tokens_per_second\", hue=\"model\")\n", - "plt.xticks(rotation=90)\n", - "plt.title(\"Throughput by Document and Model\")\n", - "plt.tight_layout()\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fc30cd3b", - "metadata": {}, - "outputs": [], - "source": [ - "order = sorted(df[\"model\"].unique())\n", - "order" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c9ef6921", - "metadata": {}, - "outputs": [], - "source": [ - "i = 1\n", - "for doc in df[\"doc_path\"].unique():\n", - " df_doc = df[df[\"doc_path\"] == doc]\n", - " doc_name = doc.split(\"/\")[-1]\n", - " plt.figure(figsize=(10, 6))\n", - " sns.boxplot(\n", - " data=df_doc, x=\"model\", y=\"tokens_per_second\", hue=\"device\", order=order\n", - " )\n", - " plt.xticks(rotation=90)\n", - " plt.grid(True)\n", - " plt.title(f\"Fig. {i}: {doc_name}\")\n", - " plt.tight_layout()\n", - " i += 1\n", - " plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "8977b125", - "metadata": {}, - "source": [ - "#### Summary comparison (same document)" - ] - }, - { - "cell_type": "markdown", - "id": "31ba2818", - "metadata": {}, - "source": [ - "##### cuda vs llama (same model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c76d769", - "metadata": {}, - "outputs": [], - "source": [ - "last_N = 1000\n", - "document = \"1- FLORES ABARCA, Francisco Alexander s 149 bis J-01-00369422-0-2022-1 Sala Feria (II) nnya vict do apela pp.pdf\"\n", - "model = \"llama3.2:3b\"\n", - "devices = [\"cuda\", \"cpu\"]\n", - "df_doc = df[df[\"doc_path\"] == document]\n", - "for d in devices:\n", - " df_filtered = df_doc[\n", - " (df[\"model\"] == model)\n", - " & (df[\"device\"] == d)\n", - " & (df[\"system_prompt_type\"] == \"template\")\n", - " ]\n", - " print(\n", - " f\"--------------------- Document: {document}, device= {d}, model= {model}---------------------\"\n", - " )\n", - " text = df_filtered[\"chat_response\"].astype(str).fillna(\"\")[-last_N:].values[0]\n", - " print(text)\n", - " print(\"\\n\")" - ] - }, - { - "cell_type": "markdown", - "id": "d1afcf25", - "metadata": {}, - "source": [ - " **Interpretation**\n", - "- Running the same model (Llama 3.2:3b) and same prompt/document in cuda is 5–6× faster than in cpu\n", - "- Despite the speedup, output content remains consistent, with both versions extracting similar entities and structure.\n", - "- The CUDA output finishes much faster but tends to include slightly more redundant or verbose entities (e.g., repeated names), suggesting minor decoding variability due to parallelization.\n", - "- The CPU output is slower but more stable and concise, with slightly better formatting consistency.\n", - "\n", - "This comparison confirms that CUDA significantly improves throughput without major semantic drift, though some token-level differences may appear." - ] - }, - { - "cell_type": "markdown", - "id": "699a16de", - "metadata": {}, - "source": [ - "##### gemma vs llama (cuda)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4ad69da3", - "metadata": {}, - "outputs": [], - "source": [ - "last_N = 1000\n", - "document = \"aymurai - ejemplo 02.docx\"\n", - "models = [\"phi3:3.8b\", \"gemma3:12b\"]\n", - "df_doc = df[df[\"doc_path\"] == document]\n", - "for m in models:\n", - " df_filtered = df_doc[\n", - " (df[\"model\"] == m)\n", - " & (df[\"device\"] == \"cuda\")\n", - " & (df[\"system_prompt_type\"] == \"template\")\n", - " ]\n", - " print(\n", - " f\"--------------------- Document: {document}, model= {m}---------------------\"\n", - " )\n", - " text = df_filtered[\"chat_response\"].astype(str).fillna(\"\")[-last_N:].values[0]\n", - " print(text)\n", - " print(\"\\n\")" - ] - }, - { - "cell_type": "markdown", - "id": "5d7a976c", - "metadata": {}, - "source": [ - "- Both models now generate coherent, well-structured summaries capturing key actors, context, and applied laws.\n", - "Phi 3:3.8b stands out for its richer narrative detail , like “Ana Carolina Rodríguez (DNI 34.112.456) y su hija menor M.F.R”\n", - "\n", - "- Gemma 3:12b produces a clearer and more factual summary, focusing on explicit information and structured entities.\n", - "- Phi offers greater completeness and contextual depth, while Gemma achieves higher precision and speed.\n", - "Overall, they show complementary strengths: Phi excels in descriptive depth, Gemma in concise factual extraction." - ] - }, - { - "cell_type": "markdown", - "id": "182387a0", - "metadata": {}, - "source": [ - "##### Same document, phi3 vs. phi4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8ea9f690", - "metadata": {}, - "outputs": [], - "source": [ - "df.columns" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "31fb2d90", - "metadata": {}, - "outputs": [], - "source": [ - "set(df.model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cfece56c", - "metadata": {}, - "outputs": [], - "source": [ - "set(df.doc_path)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e1296370", - "metadata": {}, - "outputs": [], - "source": [ - "df.columns" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "63a4ef9d", - "metadata": {}, - "outputs": [], - "source": [ - "last_N = 1000\n", - "document = \"aymurai - ejemplo 02.docx\"\n", - "models = [\"phi3:3.8b\", \"phi4:14b\"]\n", - "df_doc = df[df[\"doc_path\"] == document]\n", - "for m in models:\n", - " df_filtered = df_doc[\n", - " (df[\"model\"] == m)\n", - " & (df[\"device\"] == \"cuda\")\n", - " & (df[\"system_prompt_type\"] == \"template\")\n", - " ]\n", - " print(\n", - " f\"--------------------- Document: {document}, model= {m}---------------------\"\n", - " )\n", - " text = df_filtered[\"chat_response\"].astype(str).fillna(\"\")[-last_N:].values[0]\n", - " print(text)\n", - " print(\"\\n\")" - ] - }, - { - "cell_type": "markdown", - "id": "e078eef3", - "metadata": {}, - "source": [ - "When comparing phi3:3.8b and phi4:14b on the same document, the improvement in phi4 is evident both in structure and content quality. \n", - "\n", - "While phi3 tends to generate verbose outputs and include unnecessary placeholders (e.g., “not mentioned in the document provided”), phi4 produces concise, relevant, and well-organized information. \n", - "\n", - "Its summaries remain focused on the extracted entities without overgenerating text, which improves readability and coherence. The token usage, shown in Fig. 4, also confirm this efficiency: phi4 requires fewer output tokens while maintaining higher generation speed and precision in content extraction." - ] - }, - { - "cell_type": "markdown", - "id": "aebe3666", - "metadata": {}, - "source": [ - "#### Tokens/sec by document: cuda and cpu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2023483d", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import pandas as pd\n", - "\n", - "# Compute mean and std per document/model/device\n", - "df_summary = (\n", - " df.groupby([\"doc_path\", \"model\", \"device\"])[\"tokens_per_second\"]\n", - " .agg([\"mean\", \"std\", \"count\"])\n", - " .reset_index()\n", - ")\n", - "df_summary[\"sem\"] = df_summary[\"std\"] / df_summary[\"count\"] ** 0.5 # standard error\n", - "\n", - "# Sort models for consistent order\n", - "# order = sorted(df_summary['model'].unique())\n", - "\n", - "# --- Plot for each device type ---\n", - "for device in [\"cuda\", \"cpu\"]:\n", - " df_device = df_summary[df_summary[\"device\"] == device]\n", - "\n", - " plt.figure(figsize=(10, 6))\n", - "\n", - " # Plot one line per document\n", - " for doc, df_doc in df_device.groupby(\"doc_path\"):\n", - " doc_name = doc.split(\"/\")[-1][:20]\n", - " plt.errorbar(\n", - " df_doc[\"model\"],\n", - " df_doc[\"mean\"],\n", - " yerr=df_doc[\"sem\"],\n", - " fmt=\"-o\",\n", - " capsize=3,\n", - " alpha=0.7,\n", - " label=doc_name,\n", - " )\n", - "\n", - " plt.xticks(rotation=90)\n", - " plt.xlabel(\"Model\")\n", - " plt.ylabel(\"Tokens per second (mean ± SEM)\")\n", - " plt.title(f\"Performance across documents ({device.upper()})\")\n", - " plt.grid(True, linestyle=\"--\", alpha=0.6)\n", - " plt.legend(\n", - " title=\"Document\", fontsize=\"small\", loc=\"best\"\n", - " ) # bbox_to_anchor=(1.05, 1), loc='upper left')\n", - " plt.tight_layout()\n", - " plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2fbe4fdc", - "metadata": {}, - "source": [ - "**Overall speed:**\n", - "- On CUDA, generation speed ranges from ~10 to 90 tokens/sec — up to 10× faster than CPU.\n", - "- On CPU, all models stay below 13 tokens/sec, showing clear hardware dependency.\n", - " \n", - "**Model comparison:**\n", - "- Llama 3.2:3b consistently achieves the highest speed across both devices, though its summary quality is limited.\n", - "- Phi 3/4 and DeepSeek 1.1b are among the slowest, likely due to heavier contextual reasoning or token complexity — however, DeepSeek often fails to summarize properly, repeating or looping.\n", - "- Gemma 3:12b and Gemma 3:4b show balanced performance, maintaining moderate speed and stable behavior across documents.\n", - "\n", - "**Document variability:**\n", - "- Speed variation across documents is low, indicating that model performance is robust to input differences.\n", - "- Slight dips appear for longer or denser texts, especially in CPU runs.\n", - " \n", - "**Key insight:**\n", - "- CUDA acceleration drastically improves throughput without affecting consistency across documents.\n", - "- Llama remains the best trade-off between speed and reliability, while Phi and Gemma prioritize content quality over raw speed." - ] - }, - { - "cell_type": "markdown", - "id": "0b721074", - "metadata": {}, - "source": [ - "### Output outliers/hallucinations visualizations" - ] - }, - { - "cell_type": "markdown", - "id": "c3a3c149", - "metadata": {}, - "source": [ - "We see the last 100 characters of each output that was truncated (df['output_tokens']>16000)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ca18869a", - "metadata": {}, - "outputs": [], - "source": [ - "N = 100\n", - "for idx, txt in enumerate(df_truncated[\"chat_response\"].astype(str).fillna(\"\")):\n", - " print(\n", - " f\"+++++++++++++ Truncated summary {idx} (model={df_truncated.iloc[idx].get('model', '')}, system_prompt_type={df_truncated.iloc[idx].get('system_prompt_type', '')}, doc_path={df_truncated.iloc[idx].get('doc_path', '')}) +++++++++++++\"\n", - " )\n", - " text = txt[-N:]\n", - " print(text)\n", - " print(\"\\n\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aed870a3", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai", - "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.10.15" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/summarization/03-summarization-endpoint.ipynb b/notebooks/experiments/summarization/03-summarization-endpoint.ipynb deleted file mode 100644 index 059248c0..00000000 --- a/notebooks/experiments/summarization/03-summarization-endpoint.ipynb +++ /dev/null @@ -1,170 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "d636dee3", - "metadata": {}, - "source": [ - "# Summarization API endpoint" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "53cbedeb", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "import os\n", - "\n", - "import requests\n", - "from rich.pretty import pprint" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24dfb1f2", - "metadata": {}, - "outputs": [], - "source": [ - "API_BASE_URL = os.getenv(\"AYMURAI_API_BASE_URL\", \"http://localhost:8999\")\n", - "SUMMARIZE_URL = f\"{API_BASE_URL}/llm/summarize\"\n", - "print(f\"POST {SUMMARIZE_URL}\")" - ] - }, - { - "cell_type": "markdown", - "id": "2be189f4", - "metadata": {}, - "source": [ - "## Build request payload" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b547916", - "metadata": {}, - "outputs": [], - "source": [ - "sample_text = \"\"\"\n", - "La inteligencia artificial (IA) ha revolucionado numerosos campos, desde la medicina hasta la industria del entretenimiento,\n", - "y el ámbito de la justicia no es una excepción. La IA tiene el potencial de transformar la forma en que se administran los\n", - "sistemas legales, mejorando la eficiencia y la precisión en la toma de decisiones judiciales. Sin embargo, también plantea\n", - "desafíos éticos y legales significativos que deben ser abordados cuidadosamente.\n", - "\"\"\".strip()\n", - "\n", - "payload = {\n", - " \"text\": sample_text,\n", - " \"model\": \"llama3.2:3b\",\n", - " \"system_prompt\": \"Eres un asistente que resume textos de manera concisa y clara sin inventar información.\",\n", - " \"tokenizer\": None,\n", - " \"options\": {\"num_ctx\": 1024, \"num_predict\": 512, \"stream\": True},\n", - "}\n", - "\n", - "pprint(payload)\n" - ] - }, - { - "cell_type": "markdown", - "id": "aa2adc0d", - "metadata": {}, - "source": [ - "## Call the endpoint" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81d6d33f", - "metadata": {}, - "outputs": [], - "source": [ - "def call_summarize(text_payload: dict) -> dict:\n", - " response = requests.post(SUMMARIZE_URL, json=text_payload)\n", - " response.raise_for_status()\n", - " return response.json()\n", - "\n", - "\n", - "result = call_summarize(payload)\n", - "pprint({\"model\": result.get(\"model\"), \"chunks_used\": result.get(\"chunks_used\")})\n", - "print(\"Summary:\")\n", - "print(result.get(\"summary\", \"\"))\n", - "\n", - "print(\"Steps:\")\n", - "pprint(result.get(\"steps\", []))" - ] - }, - { - "cell_type": "markdown", - "id": "d3402133", - "metadata": {}, - "source": [ - "## Streamed call" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aaa18c09", - "metadata": {}, - "outputs": [], - "source": [ - "STREAM_URL = f\"{API_BASE_URL}/llm/summarize/stream\"\n", - "\n", - "\n", - "def stream_summarize(text_payload: dict) -> None:\n", - " with requests.post(STREAM_URL, json=text_payload, stream=True, timeout=120) as resp:\n", - " resp.raise_for_status()\n", - " for raw_line in resp.iter_lines():\n", - " if not raw_line:\n", - " continue\n", - " if not raw_line.startswith(b\"data: \"):\n", - " continue\n", - " event = json.loads(raw_line.split(b\"data: \", 1)[1])\n", - " if event.get(\"type\") == \"token\":\n", - " print(event.get(\"text\", \"\"), end=\"\", flush=True)\n", - " elif event.get(\"type\") == \"summary\":\n", - " print(\"\\n\\n---\\nFinal summary:\\n\")\n", - " print(event.get(\"summary\", \"\"))\n", - " print(\"\\nSteps:\", event.get(\"steps\", []))\n", - " else:\n", - " print(f\"\\n[{event.get('type')}] {event}\")\n", - "\n", - "\n", - "stream_summarize(payload)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d14bc8be", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai (3.10.19)", - "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.10.19" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb b/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb deleted file mode 100644 index f54a1005..00000000 --- a/notebooks/experiments/summarization/04-dspy-prompt-optimization.ipynb +++ /dev/null @@ -1,955 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "0bf2c8f0", - "metadata": {}, - "outputs": [], - "source": [ - "# !uv add dspy" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fab6180c", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext rich" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c1734177", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "import dspy\n", - "\n", - "from aymurai.utils.json_data import load_json" - ] - }, - { - "cell_type": "markdown", - "id": "011093f3", - "metadata": {}, - "source": [ - "## Fetch data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2d546951", - "metadata": {}, - "outputs": [], - "source": [ - "labels_dir = \"/workspace/notebooks/experiments/entity-disambiguation/entities-reviewed\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4dd3dbc2", - "metadata": {}, - "outputs": [], - "source": [ - "filenames = os.listdir(labels_dir)\n", - "filenames[:1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60e2e77a", - "metadata": {}, - "outputs": [], - "source": [ - "filename = filenames[0]\n", - "label_data = load_json(os.path.join(labels_dir, filename))\n", - "label_data" - ] - }, - { - "cell_type": "markdown", - "id": "43684f12", - "metadata": {}, - "source": [ - "## Documents" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c2c389b4", - "metadata": {}, - "outputs": [], - "source": [ - "import mimetypes\n", - "import time\n", - "from pathlib import Path\n", - "from typing import Iterable\n", - "\n", - "import requests" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ebc22f54", - "metadata": {}, - "outputs": [], - "source": [ - "API_BASE_URL = os.getenv(\"API_BASE_URL\", \"http://localhost:8899\")\n", - "ENDPOINT = f\"{API_BASE_URL}/misc/document-extract\"\n", - "DATA_ROOT = Path(\n", - " os.getenv(\"DOCUMENT_DATA_ROOT\", \"/resources/data/restricted/summarization\")\n", - ")\n", - "DOC_EXTENSIONS = {\".pdf\", \".docx\"}\n", - "REQUEST_TIMEOUT = float(os.getenv(\"REQUEST_TIMEOUT\", \"30\"))\n", - "\n", - "print(f\"Target endpoint: {ENDPOINT}\")\n", - "print(f\"Data root: {DATA_ROOT.resolve()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78f2d559", - "metadata": {}, - "outputs": [], - "source": [ - "if not DATA_ROOT.exists():\n", - " raise FileNotFoundError(\n", - " f\"Directory '{DATA_ROOT}' not found. Update DATA_ROOT before continuing.\"\n", - " )\n", - "\n", - "\n", - "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", - " extensions = {ext.lower() for ext in extensions}\n", - " return sorted(\n", - " path\n", - " for path in root.rglob(\"*\")\n", - " if path.is_file() and path.suffix.lower() in extensions\n", - " )\n", - "\n", - "\n", - "documents = discover_documents(DATA_ROOT, DOC_EXTENSIONS)\n", - "print(f\"Discovered {len(documents)} documents.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "04c89e3b", - "metadata": {}, - "outputs": [], - "source": [ - "def call_extraction_api(\n", - " session: requests.Session, file_path: Path\n", - ") -> dict[str, object]:\n", - " payload: dict[str, object] = {\n", - " \"path\": str(file_path),\n", - " \"status\": \"failure\",\n", - " \"status_code\": None,\n", - " \"elapsed_s\": None,\n", - " \"detail\": None,\n", - " }\n", - "\n", - " if not file_path.exists():\n", - " payload[\"detail\"] = \"File does not exist\"\n", - " return payload\n", - "\n", - " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", - " files = {\n", - " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", - " }\n", - "\n", - " try:\n", - " start = time.perf_counter()\n", - " response = session.post(\n", - " ENDPOINT,\n", - " files=files,\n", - " timeout=REQUEST_TIMEOUT,\n", - " )\n", - " elapsed = time.perf_counter() - start\n", - " except requests.RequestException as exc:\n", - " payload[\"detail\"] = f\"Request failed: {exc}\"\n", - " return payload\n", - " finally:\n", - " files[\"file\"][1].close()\n", - "\n", - " payload[\"status_code\"] = response.status_code\n", - " payload[\"elapsed_s\"] = elapsed\n", - "\n", - " try:\n", - " response_body = response.json()\n", - " except ValueError:\n", - " response_body = {\"raw\": response.text[:500]}\n", - "\n", - " if response.ok:\n", - " payload[\"status\"] = \"success\"\n", - " payload[\"detail\"] = {\n", - " \"document_id\": response_body.get(\"document_id\"),\n", - " \"document\": response_body.get(\"document\", []),\n", - " }\n", - " else:\n", - " payload[\"detail\"] = response_body\n", - "\n", - " return payload" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08b9ebb8", - "metadata": {}, - "outputs": [], - "source": [ - "doc_path = Path(os.path.join(DATA_ROOT, \"aymurai - ejemplo 01.docx\"))\n", - "extracted_document = call_extraction_api(requests.Session(), doc_path)\n", - "document = \"\\n\".join(extracted_document[\"detail\"][\"document\"])\n", - "print(document)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f6f722d5", - "metadata": {}, - "outputs": [], - "source": [ - "references = load_json(os.path.join(labels_dir, filename))\n", - "references" - ] - }, - { - "cell_type": "markdown", - "id": "137a89c6", - "metadata": {}, - "source": [ - "## Evaluation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e9b03a8", - "metadata": {}, - "outputs": [], - "source": [ - "# lm = dspy.LM(\"ollama_chat/gpt-oss:20b\", api_base=\"http://localhost:11434\")\n", - "lm = dspy.LM(\"ollama_chat/qwen3:8b\", api_base=\"http://localhost:11434\")\n", - "dspy.configure(lm=lm)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81c419f3", - "metadata": {}, - "outputs": [], - "source": [ - "print(document)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9825ac86", - "metadata": {}, - "outputs": [], - "source": [ - "# qwen3:14b\n", - "summarize = dspy.ChainOfThought(\"document -> summary\")\n", - "response = summarize(document=document)\n", - "print(response.summary)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e8b91d03", - "metadata": {}, - "outputs": [], - "source": [ - "summarize = dspy.ChainOfThought(\"document -> summary\")\n", - "response = summarize(document=document)\n", - "print(response.summary)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a17443d0", - "metadata": {}, - "outputs": [], - "source": [ - "print(response.reasoning)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15d30a8d", - "metadata": {}, - "outputs": [], - "source": [ - "from dataclasses import dataclass\n", - "\n", - "from rapidfuzz import fuzz\n", - "\n", - "\n", - "class AssessSummary(dspy.Signature):\n", - " \"\"\"High-precision assessment of legal summaries with strict grounding requirements.\"\"\"\n", - "\n", - " # Inputs\n", - " document = dspy.InputField(\n", - " desc=\"Verbatim source document. Treat it as the only ground truth; cite exact passages when justifying decisions.\"\n", - " )\n", - " summary = dspy.InputField(\n", - " desc=\"Candidate legal summary whose clarity, factual grounding, and entity coverage will be judged.\"\n", - " )\n", - " references = dspy.InputField(\n", - " desc=\"Ordered list of salient legal entities, citations, or parties expected to appear verbatim in the summary.\"\n", - " )\n", - "\n", - " # Outputs\n", - " language_ok: bool = dspy.OutputField(\n", - " desc=\"Return True only if the input summary is completely written in the same language as the document.\"\n", - " )\n", - " language_reason = dspy.OutputField(\n", - " desc=\"Identify any language mismatches or confirm consistent language use.\"\n", - " )\n", - " clarity_ok: bool = dspy.OutputField(\n", - " desc=\"Return True only if the summary is concise, logically ordered, and understandable without assuming missing context.\"\n", - " )\n", - " clarity_reason = dspy.OutputField(\n", - " desc=\"Briefly highlight phrasing strengths or point out specific sentences that harmed clarity.\"\n", - " )\n", - " factfulness_ok: bool = dspy.OutputField(\n", - " desc=\"Return True only if every claim is explicitly supported by the document. Any unsupported or extrapolated statement must flip this to False.\"\n", - " )\n", - " factfulness_reason = dspy.OutputField(\n", - " desc=\"Quote the exact evidence backing supported claims. Explicitly list each unsupported or hallucinated statement.\"\n", - " )\n", - " coverage_ok: bool = dspy.OutputField(\n", - " desc=\"Return True only if the summary mentions the expected references with correct attributes and relationships.\"\n", - " )\n", - " coverage_reason = dspy.OutputField(\n", - " desc=\"Explain which references were correctly included or omitted, explicitly citing the relevant summary spans.\"\n", - " )\n", - "\n", - "\n", - "@dataclass\n", - "class SummaryScore:\n", - " score: float\n", - " coverage_score: float\n", - " judgement: dspy.Prediction\n", - "\n", - "\n", - "class SummaryMetric(dspy.Module):\n", - " COVERAGE_THRESHOLD = 0.9\n", - "\n", - " def __call__(self, document, references, summary, trace=None):\n", - " coverage_score = self._coverage_score(summary, references)\n", - "\n", - " # with dspy.context(\n", - " # lm=dspy.LM(\"ollama_chat/gpt-oss:20b\", api_base=\"http://localhost:11434\")\n", - " # ):\n", - " # Run assessment model\n", - " judgement = self.predict(\n", - " document=document,\n", - " summary=summary,\n", - " references=references,\n", - " )\n", - "\n", - " if not judgement.language_ok:\n", - " return SummaryScore(\n", - " score=0.0,\n", - " coverage_score=coverage_score,\n", - " judgement=judgement,\n", - " )\n", - "\n", - " score = sum(\n", - " [\n", - " 0.2 * (judgement.clarity_ok or 0),\n", - " 0.2 * (judgement.factfulness_ok or 0),\n", - " 0.6 * (coverage_score * (judgement.coverage_ok or 0)),\n", - " ]\n", - " )\n", - "\n", - " return SummaryScore(\n", - " score=score,\n", - " coverage_score=coverage_score,\n", - " judgement=judgement,\n", - " )\n", - "\n", - " def predict(self, document, summary, references):\n", - " return dspy.Predict(AssessSummary)(\n", - " document=document,\n", - " summary=summary,\n", - " references=references,\n", - " )\n", - "\n", - " def _coverage_score(\n", - " self, summary: str, references, threshold: float | None = None\n", - " ) -> float:\n", - " \"\"\"\n", - " Calculate the coverage score of the summary against the reference entities.\n", - "\n", - " Args:\n", - " summary (str): The summary text to be evaluated for coverage.\n", - " references (list): The list of reference entities expected to be covered in the summary.\n", - " threshold (float | None): The minimum similarity threshold to consider an entity as covered.\n", - " Defaults to COVERAGE_THRESHOLD.\n", - "\n", - " Returns:\n", - " float: The coverage score representing how well the summary covers the reference entities.\n", - " \"\"\"\n", - " if not references:\n", - " return 1.0\n", - "\n", - " threshold = threshold or self.COVERAGE_THRESHOLD\n", - " summary_lower = summary.lower()\n", - " total_score, counted = 0.0, 0\n", - "\n", - " for ref in references:\n", - " aliases = list(ref.get(\"aliases\") or [])\n", - " canonical = ref.get(\"canonical_text\")\n", - " if canonical:\n", - " aliases = [canonical, *aliases]\n", - "\n", - " aliases = set(alias.strip().lower() for alias in aliases if alias)\n", - "\n", - " best = max(\n", - " fuzz.partial_ratio(alias.lower(), summary_lower) / 100.0\n", - " for alias in aliases\n", - " )\n", - " if best < threshold:\n", - " best = 0.0\n", - "\n", - " total_score += best\n", - " counted += 1\n", - "\n", - " return total_score / counted if counted else 0.0\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e6607ae5", - "metadata": {}, - "outputs": [], - "source": [ - "metric = SummaryMetric()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f43d5278", - "metadata": {}, - "outputs": [], - "source": [ - "# phi3:14b\n", - "score = metric(document, references, response.summary)\n", - "score" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "636d3124", - "metadata": {}, - "outputs": [], - "source": [ - "score = metric(document, references, response.summary)\n", - "score" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2e23a09", - "metadata": {}, - "outputs": [], - "source": [ - "summarization_results = load_json(\n", - " DATA_ROOT / \"summarization-benchmark-results-cuda.json\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52a0ef90", - "metadata": {}, - "outputs": [], - "source": [ - "len(summarization_results)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "88820925", - "metadata": {}, - "outputs": [], - "source": [ - "summaries = [\n", - " doc\n", - " for doc in summarization_results\n", - " if doc[\"doc_path\"] == os.path.basename(doc_path)\n", - "]\n", - "\n", - "len(summaries)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb08ce45", - "metadata": {}, - "outputs": [], - "source": [ - "from tqdm import tqdm\n", - "\n", - "evaluation_results = []\n", - "\n", - "for summary_entry in tqdm(summaries):\n", - " summary = summary_entry[\"chat_response\"]\n", - " score = metric(document, references, summary)\n", - " evaluation_results.append(\n", - " {\n", - " \"doc_path\": summary_entry[\"doc_path\"],\n", - " \"summary\": summary,\n", - " \"score\": score,\n", - " \"metadata\": {\n", - " \"model\": summary_entry.get(\"model\"),\n", - " \"system_prompt_type\": summary_entry.get(\"system_prompt_type\"),\n", - " \"options\": summary_entry.get(\"options\"),\n", - " },\n", - " }\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "986029ec", - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_results = sorted(\n", - " evaluation_results, key=lambda x: x[\"score\"].score, reverse=True\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7637aa56", - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2be4980", - "metadata": {}, - "outputs": [], - "source": [ - "# Update the score recomputation\n", - "\n", - "evaluation_results_bkp = evaluation_results.copy()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55177b67", - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_results[-1][\"score\"].score = 0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4a33d896", - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_results[-1][\"score\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bac3165b", - "metadata": {}, - "outputs": [], - "source": [ - "for eval_result in evaluation_results:\n", - " summary_score = eval_result[\"score\"]\n", - " judgement = summary_score.judgement\n", - "\n", - " score = sum(\n", - " [\n", - " 0.2 * (judgement.clarity_ok or 0),\n", - " 0.2 * (judgement.factfulness_ok or 0),\n", - " 0.6 * (summary_score.coverage_score or 0),\n", - " ]\n", - " )\n", - "\n", - " summary_score.score = score" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78790a1b", - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_resultsv = sorted(\n", - " evaluation_results, key=lambda x: x[\"score\"].score, reverse=True\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea3d7601", - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "91f48ef6", - "metadata": {}, - "outputs": [], - "source": [ - "lm.history[0]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ff02fd1c", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "71898f8f", - "metadata": {}, - "source": [ - "---\n", - "## Optimization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "831a7eee", - "metadata": {}, - "outputs": [], - "source": [ - "labels_docs_map = {} # Fill in with appropriate mapping of labels to documents" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "69fa5ec5", - "metadata": {}, - "outputs": [], - "source": [ - "examples = []\n", - "\n", - "for filename, doc_path in labels_docs_map.items():\n", - " references = load_json(os.path.join(labels_dir, filename))\n", - " extracted_document = call_extraction_api(requests.Session(), Path(doc_path))\n", - " document = \"\\n\".join(extracted_document[\"detail\"][\"document\"])\n", - "\n", - " example = dspy.Example(\n", - " document=document,\n", - " references=references,\n", - " ).with_inputs(\"document\")\n", - " examples.append(example)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "307668d0", - "metadata": {}, - "outputs": [], - "source": [ - "examples[:2]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d2403a1c", - "metadata": {}, - "outputs": [], - "source": [ - "# Split examples into simple train/dev partitions\n", - "train_examples = examples[:8]\n", - "dev_examples = examples[8:]\n", - "\n", - "print(f\"Train examples: {len(train_examples)}\")\n", - "print(f\"Dev examples: {len(dev_examples)}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fe1d3d92", - "metadata": {}, - "outputs": [], - "source": [ - "class CoTSummarizer(dspy.Module):\n", - " def __init__(self):\n", - " super().__init__()\n", - " self.cot = dspy.ChainOfThought(\"document -> summary\")\n", - "\n", - " def forward(self, document):\n", - " # references flows through so optimizers can use it in demos/prompts\n", - " return self.cot(document=document)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8ce4e7b5", - "metadata": {}, - "outputs": [], - "source": [ - "metric_module = SummaryMetric()\n", - "\n", - "\n", - "def summary_metric(example, prediction, trace=None):\n", - " package = metric_module(\n", - " document=example.document,\n", - " references=example.references,\n", - " summary=prediction.summary,\n", - " trace=trace,\n", - " )\n", - "\n", - " if trace is not None:\n", - " return package.score >= 0.75\n", - "\n", - " return package.score" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a194d49a", - "metadata": {}, - "outputs": [], - "source": [ - "evaluate = dspy.Evaluate(\n", - " devset=examples,\n", - " metric=summary_metric,\n", - " num_threads=8,\n", - " display_progress=True,\n", - " display_table=True,\n", - ")\n", - "\n", - "evaluation = evaluate(\n", - " program=CoTSummarizer(),\n", - ")\n", - "\n", - "evaluation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "305f5d0f", - "metadata": {}, - "outputs": [], - "source": [ - "from dspy.teleprompt import COPRO\n", - "\n", - "model_to_generate_prompts = dspy.LM(\n", - " \"ollama_chat/gpt-oss:20b\",\n", - " api_base=\"http://localhost:11434\",\n", - " drop_params=True,\n", - ")\n", - "\n", - "copro = COPRO(\n", - " prompt_model=model_to_generate_prompts,\n", - " metric=summary_metric,\n", - ")\n", - "eval_kwargs = {\"num_threads\": 16, \"display_progress\": True, \"display_table\": 0}\n", - "\n", - "copro_program = copro.compile(\n", - " student=CoTSummarizer(),\n", - " trainset=examples,\n", - " eval_kwargs=eval_kwargs,\n", - ")\n", - "\n", - "copro_program" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "126401a0", - "metadata": {}, - "outputs": [], - "source": [ - "copro_program" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e4b7b2f1", - "metadata": {}, - "outputs": [], - "source": [ - "from dspy.teleprompt import COPRO\n", - "\n", - "model_to_generate_prompts = dspy.LM(\n", - " \"ollama_chat/gpt-oss:20b\",\n", - " api_base=\"http://localhost:11434\",\n", - " drop_params=True,\n", - ")\n", - "\n", - "copro = COPRO(\n", - " prompt_model=model_to_generate_prompts,\n", - " metric=summary_metric,\n", - ")\n", - "eval_kwargs = {\"num_threads\": 16, \"display_progress\": True, \"display_table\": 0}\n", - "\n", - "copro_program = copro.compile(\n", - " student=CoTSummarizer(),\n", - " trainset=examples,\n", - " eval_kwargs=eval_kwargs,\n", - ")\n", - "\n", - "copro_program" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "56912cdf", - "metadata": {}, - "outputs": [], - "source": [ - "copro_program" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d604c0e", - "metadata": {}, - "outputs": [], - "source": [ - "evaluate = dspy.Evaluate(\n", - " devset=examples,\n", - " metric=summary_metric,\n", - " num_threads=8,\n", - " display_progress=True,\n", - " display_table=True,\n", - ")\n", - "\n", - "\n", - "evaluation = evaluate(program=copro_program)\n", - "evaluation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6224fc48", - "metadata": {}, - "outputs": [], - "source": [ - "from dspy.teleprompt import MIPROv2\n", - "\n", - "program = CoTSummarizer()\n", - "\n", - "optimizer = MIPROv2(\n", - " prompt_model=model_to_generate_prompts,\n", - " metric=summary_metric,\n", - " auto=\"medium\",\n", - ")\n", - "\n", - "# Optimize program\n", - "print(\"Optimizing program with MIPRO...\")\n", - "optimized_program = optimizer.compile(\n", - " program.deepcopy(),\n", - " trainset=train_examples,\n", - " valset=dev_examples,\n", - " max_bootstrapped_demos=4,\n", - " max_labeled_demos=0,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2a262672", - "metadata": {}, - "outputs": [], - "source": [ - "bootstrapped_eval = evaluate(program=optimized_program)\n", - "bootstrapped_eval" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a9e63a6", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "aymurai", - "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.10.18" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From e333f4cac345b119bdc74f36fa9251d4478f41c6 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:44:31 -0300 Subject: [PATCH 058/101] =?UTF-8?q?=F0=9F=A6=96=20Changed=20pyproject.toml?= =?UTF-8?q?=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 51e6aa24..f858567c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,10 +84,6 @@ dependencies = [ "pymupdf4llm>=0.0.17", "pypandoc>=1.15", "python-docx>=1.2.0", - "langextract[openai]==1.1.0", - "ollama==0.6.1", - "tiktoken==0.12.0", - "marker-pdf==1.10.1", "docx2txt>=0.9", ] @@ -96,11 +92,6 @@ Homepage = "https://www.aymurai.info/" Repository = "https://github.com/aymurAI/backend" Issues = "https://github.com/AymurAI/backend/issues" -[dependency-groups] -mlops = [ - "mlflow>=2.12.1", - "boto3>=1.34.0", -] dev = [ "matplotlib>=3.10.0", "seaborn>=0.13.2", From f735c1b5daa437532eb86e103914828f89c57fe4 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:45:29 -0300 Subject: [PATCH 059/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20resources/llm?= =?UTF-8?q?=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/llm/entity_disambiguation.yml | 69 ------------------------ resources/llm/summarization.yml | 71 ------------------------- 2 files changed, 140 deletions(-) delete mode 100644 resources/llm/entity_disambiguation.yml delete mode 100755 resources/llm/summarization.yml diff --git a/resources/llm/entity_disambiguation.yml b/resources/llm/entity_disambiguation.yml deleted file mode 100644 index 14615b37..00000000 --- a/resources/llm/entity_disambiguation.yml +++ /dev/null @@ -1,69 +0,0 @@ -PER: - system: | - Eres un auditor experto en desambiguación de entidades legales. Tu tarea es validar y enriquecer una lista de personas pre-agrupadas. - - ### Instrucciones Estrictas: - 1. **Asignación de Rol:** Identifica el rol procesal basándote en las ventanas de contexto dentro de cada entidad canónica en la parte de `attributes`['context'] - Elija un rol EXCLUSIVAMENTE en esta lista: - - "Denunciante", "Denunciado/a", "Víctima", "Juez/a", "Defensor/a de Cámara", "Asesor/a Tutelar", "Fiscal", "Abogado/a", "Perito/a", "Testigo". - 2. **Regla "Dr/a":** Si se menciona como "Dr.", "Dra." o "Dres." y no hay otro cargo explícito, asígnale el rol "Abogado/a". - 3. **Validación y Fusión:** - Usa los fragmentos de contexto para confirmar que todos los aliases de un grupo pertenecen a la misma persona. - - Si detectas que dos entidades distintas son en realidad la misma persona (ej: un grupo con el nombre completo y otro con iniciales o cargo), fusiónalos. - - Si un alias dentro de un grupo pertenece a una persona diferente, sepáralo. - 4. **Filtrado:** Elimina cualquier entidad que no sea una persona física (ej: instituciones, direcciones, leyes). - 5. **Limpieza:** Limpiá los `aliases` y el `canonical_text` si los mismos tienen otras palabras, pero respeta que estén escritos de igual manera a que sus ventanas de contexto. - Te dejo un ejemplo de esta instrucción. - Vos recibís: - { - "canonical_text": "por DREXLER, JORGE", - "aliases": [ - "por DREXLER, JORGE", - "Jorge Drexler" - ], - "attributes": { - "context": [ - "Fdo. por DREXLER, JORGE - JUEZ DE CÁMARA el mismo cita en el documento 56 que Juan es culpable", - "es acaso Jorge Drexler el juez designado para esta causa" - ] - } - - Debés entregar: - { - "canonical_text": "DREXLER, JORGE", - "aliases": [ - "DREXLER, JORGE", - "Jorge Drexler" - ], - "attributes": { - "role": "Juez/a" - } - - 6. **Manejo de Iniciales:** Identifica si las siglas (ej: "M.L.") corresponden a una persona con nombre completo en el listado y únelas si el contexto lo confirma. - - ### Formato de Salida (JSON): - Devuelve un array de objetos con esta estructura: - [ - { - "entity_id": "optional-unique-id", - "aymurai_label": "PER", - "canonical_text": "Nombre de la entidad", - "aliases": [ - "Nombre de la entidad", - "Alias 1", - "Alias 2" - ], - "attributes": { - "role": "Rol de la lista o null" - } - } - ] - - No incluyas el campo 'context' en tu respuesta final. - - user: | - Se proporciona la lista de entidades (personas) pre-clusterizadas. Cada una incluye en `attributes['context']` las ventanas de texto de ±120 caracteres donde fue mencionada en la causa judicial para que puedas determinar su rol y validez. - - # Entidades Pre-clusterizadas a validar: - {canonical_entities} - - Procesa la lista siguiendo las instrucciones de filtrado, fusión y asignación de roles. diff --git a/resources/llm/summarization.yml b/resources/llm/summarization.yml deleted file mode 100755 index 78a502fc..00000000 --- a/resources/llm/summarization.yml +++ /dev/null @@ -1,71 +0,0 @@ -system-prompts: - baseline: | - Eres un asistente especializado en resumir documentos. - Tu único objetivo es producir un resumen fiel, claro y conciso del texto que recibirás. - No añadas interpretaciones ni conclusiones no presentes en el documento. - - task-specific: | - Eres un analista legal especializado en elaborar resúmenes de resoluciones y documentos jurídicos. - Tu tarea es condensar la información esencial: hechos, cuestiones jurídicas, fundamentos y decisión. - Mantén precisión terminológica y evita inferencias no textuales. Si algún elemento no aparece en el texto, no lo inventes. - - template: | - Eres un asistente especializado en el análisis de documentos judiciales. - Debes elaborar un resumen conciso del texto recibido, priorizando la información más relevante e identificando: - - {information_to_extract} - - Además, cuando el texto lo permita, debes reconocer y listar las siguientes entidades: - - {entities_to_identify} - - # Formato de salida (Markdown) - - ## Resumen del documento - [Descripción narrativa: hechos principales, cuestiones jurídicas, fundamentos legales y decisión.] - - ## Entidades relevantes - [Listado de entidades identificadas, agrupadas por tipo y, cuando sea posible, indicando relaciones o correspondencias.] - -defaults: - information-to-extract: | - - Hechos relevantes (qué ocurrió). - - Actores clave (quiénes están involucrados). - * Denunciante(s). - * Acusado/a(s). - * Testigo/a(s). - * Juez/a(s). - * Abogado/a(s). - * Fiscal(es). - * Perito(s). - * Otros (especificar). - - Cronología (cuándo ocurrieron los hechos y eventos procesales importantes). - - Lugar(es) (dónde ocurrieron los hechos y eventos procesales importantes). - - Fundamentos (normas legales, precedentes judiciales, doctrinas aplicadas). - - Decisión (resultado/medida adoptada). - - entities-to-identify: | - - "BANCO": Nombre de una entidad bancaria, pública o privada. - - "CBU": Código Bancario Uniforme (22 dígitos) de una cuenta. - - "CORREO_ELECTRONICO": Dirección de correo electrónico. - - "CUIJ": Código Único de Identificación Jurídica de causas judiciales. - - "CUIT_CUIL": Número de CUIT o CUIL de una persona física o jurídica. - - "DIRECCION": Dirección específica (calle, número, intersección de calles y/o avenidas, código postal, etc.). - - "DNI": Número de Documento Nacional de Identidad u otro documento identificatorio. - - "EDAD": Edad explícita de una persona. - - "ESTUDIOS": Nivel o institución educativa que permita identificar a la persona (ej. "primario incompleto", "secundario completo", "Licenciado en…"). - - "FECHA": Fecha completa o parcial (día, mes y/o año). - - "IP": Dirección IP (IPv4 o IPv6). - - "LINK": Enlace o URL a una página web. - - "LOC": Localización geográfica específica (país, provincia, estado, ciudad, localidad, barrio, etc.). - - "MARCA_AUTOMOVIL": Marca de un vehículo (ej. Toyota, Ford, Suzuki, etc.). - - "NACIONALIDAD": Nacionalidad de una persona (ej. "argentino", "brasileña"). - - "NOMBRE_ARCHIVO": Nombre completo de un archivo digital (incluyendo extensión). - - "NUM_ACTUACION": Número identificatorio de una actuación administrativa o contravencional. - - "NUM_CAJA_AHORRO": Número completo de una caja de ahorro o cuenta bancaria. - - "NUM_EXPEDIENTE": Número de expediente judicial o administrativo. - - "NUM_MATRICULA": Número de matrícula profesional o académica. - - "PATENTE_DOMINIO": Patente o dominio de un vehículo. - - "PER": Nombre(s) y apellido(s) de una persona física. Los nombres inicializados (ej., "M.T.G") y los apodos (ej., "el Gato") también cuentan como información sensible a anonimizar. - - "TELEFONO": Número telefónico (fijo o celular). - - "USUARIX": Nombre de usuario o "handle" en redes sociales o plataformas digitales. From 568496e01ec68d2bd305aeb702141533cbc6008a Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:48:24 -0300 Subject: [PATCH 060/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20summarization?= =?UTF-8?q?=5Fapp=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- summarization_app/app.py | 759 ------------------------- summarization_app/run_streamlit_app.sh | 10 - 2 files changed, 769 deletions(-) delete mode 100755 summarization_app/app.py delete mode 100755 summarization_app/run_streamlit_app.sh diff --git a/summarization_app/app.py b/summarization_app/app.py deleted file mode 100755 index e5404bc8..00000000 --- a/summarization_app/app.py +++ /dev/null @@ -1,759 +0,0 @@ -import json -import re -import textwrap -from pathlib import Path - -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns -import streamlit as st - -# Set page configuration -st.set_page_config( - page_title="Document Summarization Experiments", - page_icon="📄", - layout="wide", -) - - -# Function to load the results -@st.cache_data -def load_data(file_path: str): - try: - with open(file_path, "r") as f: - data = json.load(f) - return data - except Exception as e: - st.error(f"Error loading data: {e}") - return [] - - -# Function to extract unique values from the dataset -def get_unique_values(data, field): - values = set() - for item in data: - if field in item: - values.add(item[field]) - return sorted(list(values)) - - -# Create a helper function to handle None values -def get_sort_key(item, field, sort_order): - value = item.get(field) - # Handle None values by putting them at the end - if value is None: - return float("inf") if sort_order == "Ascending" else float("-inf") - return value - - -# Mapping from UI names to data fields for sorting and plotting -SORT_FIELD_MAP = { - "Model": "model", - "Prompt Type": "system_prompt_type", - "Tokens per Second": "tokens_per_second", - "Duration": "measured_duration_ms", - "Input Tokens": "input_tokens", - "Output Tokens": "output_tokens", - "Device": "device", -} - - -# Attempt to identify parameter scale (in billions) from model names -def get_model_parameter_scale(model_name): - if not model_name: - return None - - known_params = { - "gemma3:12b": 12, - "gemma3:40b": 40, - "gemma3:20b": 20, - "gemma3:22b": 22, - "gemma3:38b": 38, - "llama3.1:8b": 8, - "llama3.2:3b": 3, - "llama3.2:11b": 11, - "deepseek-r1:1.1b": 1.1, - "deepseek-r1:1b": 1, - "phi-3:3.8b": 3.8, - "phi-4:14b": 14, - "qwen3:3.8b": 3.8, - "qwen3:14b": 14, - "qwen3:72b": 72, - } - - model_name_lower = model_name.lower() - if model_name_lower in known_params: - return known_params[model_name_lower] - - match = re.search(r"(\d+(?:\.\d+)?)\s*b", model_name_lower) - if match: - try: - return float(match.group(1)) - except ValueError: - return None - return None - - -# Main function -def main(): - st.title("📄 Document Summarization Experiments") - - # File selector - results_file = st.sidebar.file_uploader("Upload JSON results file", type=["json"]) - - if results_file is not None: - # Load data from uploaded file - content = results_file.getvalue().decode("utf-8") - data = json.loads(content) - else: - # Try to load from the default path - default_path = "./summarization-benchmark-results.json" - if Path(default_path).exists(): - data = load_data(default_path) - st.sidebar.success(f"Loaded data from {default_path}") - else: - st.sidebar.warning("Please upload a JSON results file") - st.stop() - - # Extract unique values for filters - models = get_unique_values(data, "model") - prompt_types = get_unique_values(data, "system_prompt_type") - doc_paths = get_unique_values(data, "doc_path") - doc_filenames = {doc_path: Path(doc_path).name for doc_path in doc_paths} - devices = get_unique_values(data, "device") - - # Create filters in the sidebar - st.sidebar.header("Filters") - - model_options = ["All"] + models - default_models = ["All"] if models else [] - selected_models = st.sidebar.multiselect( - "Select Models", options=model_options, default=default_models - ) - selected_prompt_type = st.sidebar.selectbox( - "Select Prompt Type", ["All"] + prompt_types - ) - selected_doc = st.sidebar.selectbox( - "Select Document", ["All"] + list(doc_filenames.values()) - ) - selected_device = st.sidebar.selectbox("Select Device", ["All"] + devices) - - # Add metrics display option - st.sidebar.header("Display Options") - show_metrics = st.sidebar.checkbox("Show Metrics", value=True) - show_system_prompt = st.sidebar.checkbox("Show System Prompt", value=False) - show_raw_response = st.sidebar.checkbox("Show Raw Response", value=False) - - # Sorting options - st.sidebar.header("Sorting") - sort_by = st.sidebar.selectbox( - "Sort by", - [ - "Default", - "Model", - "Prompt Type", - "Tokens per Second", - "Duration", - "Input Tokens", - "Output Tokens", - "Device", - ], - ) - sort_order = st.sidebar.radio("Sort order", ["Ascending", "Descending"]) - - # Filter data based on selections - filtered_data = data - - if "All" in selected_models: - if len(selected_models) == 1: - selected_models_filter = None - else: - selected_models_filter = { - model for model in selected_models if model != "All" - } - else: - selected_models_filter = set(selected_models) - - if selected_models_filter: - filtered_data = [ - item - for item in filtered_data - if item.get("model") in selected_models_filter - ] - - if selected_prompt_type != "All": - filtered_data = [ - item - for item in filtered_data - if item.get("system_prompt_type") == selected_prompt_type - ] - - if selected_doc != "All": - filtered_data = [ - item - for item in filtered_data - if doc_filenames.get(item.get("doc_path")) == selected_doc - ] - - if selected_device != "All": - filtered_data = [ - item for item in filtered_data if item.get("device") == selected_device - ] - - # Apply sorting - if sort_by != "Default": - field = SORT_FIELD_MAP.get(sort_by) - if field: - # Sort the filtered data - filtered_data = sorted( - filtered_data, - key=lambda item: get_sort_key(item, field, sort_order), - reverse=(sort_order == "Descending"), - ) - - # Display results count - st.write(f"Found {len(filtered_data)} result(s) out of {len(data)} total.") - - # Pagination controls - st.sidebar.header("Pagination") - items_per_page_options = [5, 10, 20, 50, 100, "All"] - - # Create session state for items per page if it doesn't exist - if "items_per_page" not in st.session_state: - st.session_state.items_per_page = 10 - - # Handle the items per page selection - selected_items = st.sidebar.selectbox( - "Results per page", - options=items_per_page_options, - index=items_per_page_options.index( - 10 if 10 in items_per_page_options else items_per_page_options[1] - ), - key="items_per_page_selector", - ) - - # Handle the "All" option - if selected_items == "All": - items_per_page = len(filtered_data) - else: - items_per_page = selected_items - - # Update session state - if st.session_state.items_per_page != items_per_page: - st.session_state.items_per_page = items_per_page - st.session_state.current_page = ( - 1 # Reset to first page when changing items per page - ) - - total_pages = max(1, (len(filtered_data) + items_per_page - 1) // items_per_page) - - # Create session state for pagination if it doesn't exist - if "current_page" not in st.session_state: - st.session_state.current_page = 1 - - # Ensure current page is valid after filtering - if st.session_state.current_page > total_pages: - st.session_state.current_page = 1 - - # Only show pagination controls if there are multiple pages - if total_pages > 1: - col_label, col_input, _ = st.columns([1, 1, 6]) - - with col_label: - st.markdown("**Page**") - st.caption(f"1 - {total_pages}") - - with col_input: - current_page = st.number_input( - "Page selector", - min_value=1, - max_value=total_pages, - value=st.session_state.current_page, - step=1, - label_visibility="collapsed", - ) - st.session_state.current_page = current_page - else: - st.session_state.current_page = 1 - - current_page = st.session_state.current_page - - # Calculate start and end indices for the current page - start_idx = (current_page - 1) * items_per_page - end_idx = min(start_idx + items_per_page, len(filtered_data)) - - # Get the subset of data for the current page - page_data = filtered_data[start_idx:end_idx] - - # Show page information - col1, col2 = st.columns([3, 1]) - with col1: - st.write( - f"Displaying results {start_idx + 1}-{end_idx} of {len(filtered_data)}" - ) - - with col2: - # If we have multiple pages, show a page indicator - if total_pages > 1: - st.write(f"Page {current_page} of {total_pages}") - - # Display the filtered results - if page_data: - for idx, result in enumerate(page_data): - device_tag = ( - f" ({result.get('device', 'N/A')})" if result.get("device") else "" - ) - title = f"{result.get('model', 'N/A')}{device_tag} - [{result.get('system_prompt_type', 'Unknown')}] - {Path(result.get('doc_path', 'N/A')).name}" - - with st.expander(title): - # Display metrics as a table in a clean format - if show_metrics: - col1, col2 = st.columns(2) - - with col1: - st.subheader("Summarization Context") - context_info = { - "Model": result.get("model", "N/A"), - "Device": result.get("device", "N/A"), - "Prompt Type": result.get("system_prompt_type", "Unknown"), - "Document": Path(result.get("doc_path", "N/A")).name, - } - st.table(pd.DataFrame([context_info])) - - with col2: - st.subheader("Performance Metrics") - metrics = { - "Input Tokens": result.get("input_tokens", "N/A"), - "Output Tokens": result.get("output_tokens", "N/A"), - "Total Tokens": result.get("total_tokens", "N/A"), - "Tokens/Second": f"{result.get('tokens_per_second', 0):.2f}", - "Duration (sec)": f"{result.get('measured_duration_ms', 0) / 1000:.2f}", - "Device": result.get("device", "N/A"), - } - st.table(pd.DataFrame([metrics])) - - if show_system_prompt: - st.subheader("System Prompt") - st.caption( - f"Prompt type: **{result.get('system_prompt_type', 'Unknown')}**" - ) - st.code( - result.get("system_prompt", "N/A"), - language="markdown", - ) - - # Display chat response as markdown - st.subheader("Response") - st.markdown(result.get("chat_response", "No response available")) - - # Optional: Display raw response as a JSON - if show_raw_response and "raw_response" in result: - st.subheader("Raw Response") - st.json(result["raw_response"]) - - # Show a summary visualization if more than one result is selected - if len(filtered_data) > 1: - st.header("Summary Visualizations") - - # Create a DataFrame for easier analysis - df = pd.DataFrame(filtered_data) - df = df.loc[:, ~pd.Index(df.columns).duplicated()] - if "model" in df.columns: - df["model"] = df["model"].apply( - lambda value: ( - value - if isinstance(value, str) - else str(value) - if value is not None - else "Unknown" - ) - ) - if "doc_path" in df.columns: - df["document_name"] = df["doc_path"].apply( - lambda p: Path(p).name if isinstance(p, str) else "Unknown" - ) - else: - df["document_name"] = "Unknown" - df["model_params_b"] = df["model"].apply(get_model_parameter_scale) - df["total_duration_sec"] = df["measured_duration_ms"] / 1000 - - # Determine ordering for models in visualizations based on sorting - if "model" in df.columns: - model_values = df["model"].dropna().unique().tolist() - else: - model_values = [] - model_order = model_values.copy() - sort_field = SORT_FIELD_MAP.get(sort_by) - ascending = sort_order == "Ascending" - - if sort_by == "Model": - model_order = sorted(model_values, reverse=not ascending) - elif ( - sort_field - and sort_field in df.columns - and pd.api.types.is_numeric_dtype(df[sort_field]) - ): - model_metric = ( - df.groupby("model")[sort_field] - .mean() - .dropna() - .sort_values(ascending=ascending) - ) - if not model_metric.empty: - model_order = model_metric.index.tolist() - # Append any models missing from the metric (e.g., all NaNs) - remaining_models = [ - model for model in model_values if model not in model_order - ] - model_order.extend(remaining_models) - - if "model" in df.columns and not df.empty: - model_summary = ( - df.groupby("model") - .agg( - avg_input_tokens=("input_tokens", "mean"), - avg_total_duration_ms=("measured_duration_ms", "mean"), - avg_tokens_per_second=("tokens_per_second", "mean"), - runs=("model", "count"), - params_b=("model_params_b", "mean"), - ) - .reset_index() - ) - model_summary["avg_total_duration_sec"] = ( - model_summary["avg_total_duration_ms"] / 1000 - ) - default_param_scale = ( - model_summary["params_b"].median() - if not model_summary["params_b"].dropna().empty - else 1.0 - ) - model_summary["params_b"].fillna(default_param_scale, inplace=True) - model_summary["bubble_size"] = model_summary["params_b"] * 60 - else: - model_summary = pd.DataFrame() - - # Set up tabs for different visualizations - tab1, tab2, tab3, tab4 = st.tabs( - [ - "Performance Metrics", - "Token Usage", - "Prompt Analysis", - "Device Performance", - ] - ) - - with tab1: - col1, col2 = st.columns(2) - - with col1: - # Generation speed by model - fig, ax = plt.subplots(figsize=(10, 6)) - sns.barplot( - data=df, - x="model", - y="tokens_per_second", - order=model_order or None, - ax=ax, - ) - plt.title("Generation Speed by Model") - plt.xticks(rotation=45, ha="right") - plt.ylabel("Tokens per Second") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - with col2: - # Total duration by model - fig, ax = plt.subplots(figsize=(10, 6)) - sns.barplot( - data=df, - x="model", - y="measured_duration_ms", - order=model_order or None, - ax=ax, - ) - plt.title("Total Processing Time by Model") - plt.xticks(rotation=45, ha="right") - plt.ylabel("Duration (ms)") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - st.subheader("Input Tokens vs Total Duration (Average)") - if not model_summary.empty: - fig, ax = plt.subplots(figsize=(12, 6)) - ax.scatter( - model_summary["avg_input_tokens"], - model_summary["avg_total_duration_sec"], - s=model_summary["bubble_size"], - alpha=0.7, - color="#1f77b4", - edgecolors="black", - ) - for _, row in model_summary.iterrows(): - ax.text( - row["avg_input_tokens"], - row["avg_total_duration_sec"], - row["model"], - fontsize=9, - ha="left", - va="bottom", - alpha=0.9, - ) - ax.set_xlabel("Average Input Tokens") - ax.set_ylabel("Average Total Duration (s)") - ax.grid(True, linestyle="--", alpha=0.6) - st.pyplot(fig) - else: - st.info("Not enough data to render the bubble chart.") - - st.subheader("Performance Distributions") - dist_col1, dist_col2 = st.columns(2) - - with dist_col1: - fig, ax = plt.subplots(figsize=(10, 6)) - sns.violinplot( - data=df, - x="model", - y="tokens_per_second", - order=model_order or None, - inner="quartile", - ax=ax, - ) - plt.xticks(rotation=45, ha="right") - plt.ylabel("Tokens per Second") - plt.title("Tokens per Second Distribution by Model") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - with dist_col2: - fig, ax = plt.subplots(figsize=(10, 6)) - sns.violinplot( - data=df, - x="model", - y="total_duration_sec", - order=model_order or None, - inner="quartile", - ax=ax, - ) - plt.xticks(rotation=45, ha="right") - plt.ylabel("Total Duration (s)") - plt.title("Total Duration Distribution by Model") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - with tab2: - col1, col2 = st.columns(2) - - with col1: - # Input vs Output tokens - df_melted = pd.melt( - df, - id_vars=["model"], - value_vars=["input_tokens", "output_tokens"], - var_name="Token Type", - value_name="Token Count", - ) - fig, ax = plt.subplots(figsize=(10, 6)) - sns.barplot( - data=df_melted, - x="model", - y="Token Count", - order=model_order or None, - hue="Token Type", - ax=ax, - ) - plt.title("Input vs Output Tokens by Model") - plt.xticks(rotation=45, ha="right") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - with col2: - # Input processing time vs Output generation time - df_melted = pd.melt( - df, - id_vars=["model"], - value_vars=["input_duration_ms", "output_duration_ms"], - var_name="Processing Stage", - value_name="Duration (ms)", - ) - fig, ax = plt.subplots(figsize=(10, 6)) - sns.barplot( - data=df_melted, - x="model", - y="Duration (ms)", - order=model_order or None, - hue="Processing Stage", - ax=ax, - ) - plt.title("Processing Time Breakdown by Model") - plt.xticks(rotation=45, ha="right") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - with tab3: - col1, col2 = st.columns(2) - - with col1: - # Prompt type distribution - fig, ax = plt.subplots(figsize=(10, 6)) - prompt_counts = ( - df["system_prompt_type"].value_counts().reset_index() - ) - prompt_counts.columns = ["prompt_type", "count"] - sns.barplot(data=prompt_counts, x="prompt_type", y="count", ax=ax) - plt.title("Prompt Type Distribution") - plt.xticks(rotation=45, ha="right") - plt.ylabel("Count") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - with col2: - # Performance by prompt type - fig, ax = plt.subplots(figsize=(10, 6)) - sns.boxplot( - data=df, - x="system_prompt_type", - y="tokens_per_second", - ax=ax, - ) - plt.title("Performance by Prompt Type") - plt.xticks(rotation=45, ha="right") - plt.ylabel("Tokens per Second") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - st.subheader("Document Coverage by Model") - doc_counts = df.assign( - document_label=df["document_name"].apply( - lambda name: ( - textwrap.shorten(name, width=55, placeholder="…") - if isinstance(name, str) - else name - ) - ), - _count=1, - ).pivot_table( - index="document_label", - columns="model", - values="_count", - aggfunc="sum", - fill_value=0, - ) - if not doc_counts.empty: - if model_order: - ordered_cols = [ - col for col in model_order if col in doc_counts.columns - ] - remaining_cols = [ - col for col in doc_counts.columns if col not in ordered_cols - ] - doc_counts = doc_counts[ordered_cols + remaining_cols] - doc_totals = doc_counts.sum(axis=1).sort_values(ascending=False) - max_docs = 25 - if len(doc_totals) > max_docs: - doc_counts = doc_counts.loc[doc_totals.index[:max_docs]] - st.caption( - f"Showing top {max_docs} documents by experiment count." - ) - fig, ax = plt.subplots(figsize=(12, max(6, 0.4 * len(doc_counts)))) - sns.heatmap( - doc_counts, - annot=True, - fmt="d", - cmap="YlGnBu", - linewidths=0.3, - linecolor="white", - cbar=True, - ax=ax, - ) - plt.xlabel("Model") - plt.ylabel("Document") - plt.xticks(rotation=45, ha="right") - plt.yticks(rotation=0) - plt.title("Experiment Coverage per Document and Model") - st.pyplot(fig) - else: - st.info( - "Document coverage heatmap is unavailable with the current selection." - ) - - with tab4: - # Device performance analysis - st.subheader("Performance by Device") - if "device" in df.columns and not df.empty: - # Performance by device (all models combined) - col1, col2 = st.columns(2) - - with col1: - fig, ax = plt.subplots(figsize=(10, 6)) - sns.boxplot(data=df, x="device", y="tokens_per_second", ax=ax) - plt.title("Tokens per Second by Device") - plt.ylabel("Tokens per Second") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - with col2: - fig, ax = plt.subplots(figsize=(10, 6)) - sns.boxplot(data=df, x="device", y="total_duration_sec", ax=ax) - plt.title("Total Duration by Device") - plt.ylabel("Duration (seconds)") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - - # Model performance by device - st.subheader("Model Performance by Device") - fig, ax = plt.subplots(figsize=(12, 8)) - sns.barplot( - data=df, - x="model", - y="tokens_per_second", - hue="device", - order=model_order or None, - ax=ax, - ) - plt.title("Tokens per Second by Model and Device") - plt.xticks(rotation=45, ha="right") - plt.ylabel("Tokens per Second") - plt.grid(axis="y", linestyle="--", alpha=0.7) - plt.legend(title="Device") - st.pyplot(fig) - - # Device usage count - st.subheader("Device Usage Distribution") - device_counts = df["device"].value_counts().reset_index() - device_counts.columns = ["device", "count"] - fig, ax = plt.subplots(figsize=(8, 5)) - sns.barplot(data=device_counts, x="device", y="count", ax=ax) - plt.title("Number of Experiments by Device") - plt.ylabel("Count") - plt.grid(axis="y", linestyle="--", alpha=0.7) - st.pyplot(fig) - else: - st.info( - "Device performance data is unavailable with the current selection." - ) - - else: - st.info("No results match the selected filters.") - - # Add footer with instructions - st.sidebar.markdown("---") - st.sidebar.markdown("### Instructions") - st.sidebar.markdown( - """ - 1. Use the filters above to narrow down results: - - Filter by model, prompt type, document, or device (CPU/CUDA) - - Use multiple selections in the Models filter - 2. Expand result items to view details - 3. Toggle display options to show/hide metrics and raw responses - 4. Explore visualization tabs: - - Performance Metrics: Compare speed and duration between models - - Token Usage: Analyze input and output token distributions - - Prompt Analysis: Review prompt type performance - - Device Performance: Compare CPU vs CUDA performance - 5. Sort results by any metric including device type - """ - ) - - -if __name__ == "__main__": - main() diff --git a/summarization_app/run_streamlit_app.sh b/summarization_app/run_streamlit_app.sh deleted file mode 100755 index 0e419441..00000000 --- a/summarization_app/run_streamlit_app.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Set environment variables to reduce Streamlit watching -export STREAMLIT_SERVER_WATCH_DIRS="false" -export STREAMLIT_SERVER_HEADLESS="true" -export STREAMLIT_SERVER_FILE_WATCHER_TYPE="none" - -# Run Streamlit app -echo "Starting Streamlit app..." -python -m streamlit run app.py From 0d4e78edf6c6e5df592e0c8509ec396eab9bfcc0 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Thu, 19 Feb 2026 16:49:34 -0300 Subject: [PATCH 061/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20test/llm=5Fpro?= =?UTF-8?q?viders=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/llm_providers/__init__.py | 0 test/llm_providers/test_providers.py | 148 --------------------------- 2 files changed, 148 deletions(-) delete mode 100644 test/llm_providers/__init__.py delete mode 100644 test/llm_providers/test_providers.py diff --git a/test/llm_providers/__init__.py b/test/llm_providers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/llm_providers/test_providers.py b/test/llm_providers/test_providers.py deleted file mode 100644 index 9fbc64ae..00000000 --- a/test/llm_providers/test_providers.py +++ /dev/null @@ -1,148 +0,0 @@ -import unittest -from unittest.mock import AsyncMock, MagicMock, patch - -from aymurai.llm_providers import LLMProvider, LLMResponse, OllamaLLMProvider - - -class DummyProvider(LLMProvider): - def __init__(self, **kwargs): - super().__init__(model="dummy", **kwargs) - self.last_prompt = None - - def generate(self, prompt: str, **kwargs) -> LLMResponse: - self.last_prompt = prompt - return LLMResponse(text=f"echo::{prompt}") - - def stream(self, prompt, **kwargs): - return super().stream(prompt, **kwargs) - - -class FakeTokenizer: - def encode(self, text, add_special_tokens: bool = False): - return text.split() - - -class BaseProviderTests(unittest.TestCase): - def test_chunk_text_respects_token_limit(self): - provider = DummyProvider(max_context_tokens=4, chunk_overlap=1) - text = "uno dos tres cuatro cinco seis" - chunks = provider.chunk_text(text) - self.assertGreater(len(chunks), 1) - for i, chunk in enumerate(chunks): - self.assertEqual(chunk.index, i) - self.assertLessEqual(chunk.token_count, 4) - - def test_chunk_text_overlap_applied(self): - provider = DummyProvider(max_context_tokens=3, chunk_overlap=1) - text = "one two three four" - chunks = provider.chunk_text(text) - self.assertEqual([c.index for c in chunks], [0, 1]) - self.assertTrue(chunks[0].text.split()[-1] in chunks[1].text.split()) - - def test_chunk_text_fallback_count(self): - provider = DummyProvider(max_context_tokens=1) - provider._tokenizer = type("EmptyTok", (), {"encode": lambda self, t: []})() - chunks = provider.chunk_text("hello") - self.assertEqual(len(chunks), 1) - self.assertEqual(chunks[0].token_count, 0) - - -class OllamaProviderTests(unittest.TestCase): - def test_generate_builds_messages(self): - provider = OllamaLLMProvider(model="llama3", system_prompt="Sistema") - fake_response = {"message": {"content": "respuesta"}, "eval_count": 10} - with patch("aymurai.llm_providers.ollama_provider.ollama.chat") as mock_chat: - mock_chat.return_value = fake_response - response = provider.generate("Hola") - - self.assertEqual(response.text, "respuesta") - args, kwargs = mock_chat.call_args - self.assertEqual(kwargs["model"], "llama3") - self.assertEqual(kwargs["messages"][0]["role"], "system") - self.assertEqual(response.metadata.get("model"), "llama3") - self.assertEqual(response.metadata.get("provider"), "ollama") - self.assertIn("eval_count", response.metadata) - self.assertIn("eval_duration", response.metadata) - - def test_generate_forwards_keep_alive(self): - provider = OllamaLLMProvider(model="llama3", keep_alive=30) - fake_response = {"message": {"content": "ok"}} - with patch("aymurai.llm_providers.ollama_provider.ollama.chat") as mock_chat: - mock_chat.return_value = fake_response - _ = provider.generate("Hola") - _, kwargs = mock_chat.call_args - self.assertEqual(kwargs["keep_alive"], 30) - - def test_stream_yields_chunks(self): - provider = OllamaLLMProvider(model="llama3") - fake_chunks = iter( - [ - {"message": {"content": "Hola"}, "done": False}, - {"message": {"content": " Mundo"}, "done": True}, - ] - ) - with patch("aymurai.llm_providers.ollama_provider.ollama.chat") as mock_chat: - mock_chat.return_value = fake_chunks - pieces = list(provider.stream("Hola")) - - self.assertEqual([piece.text for piece in pieces], ["Hola", " Mundo"]) - _, kwargs = mock_chat.call_args - self.assertTrue(kwargs["stream"]) - self.assertEqual(pieces[-1].text, " Mundo") - self.assertTrue(pieces[-1].metadata.get("done")) - - def test_generate_raises_without_prompt_or_messages(self): - provider = OllamaLLMProvider(model="llama3") - with self.assertRaises(ValueError): - provider.generate(prompt=None, messages=None) - - -class AsyncOllamaProviderTests(unittest.IsolatedAsyncioTestCase): - async def test_async_generate_builds_messages(self): - provider = OllamaLLMProvider(model="llama3", system_prompt="Sistema") - fake_response = {"message": {"content": "respuesta"}, "eval_count": 10} - mock_client = MagicMock() - mock_client.chat = AsyncMock(return_value=fake_response) - - with patch( - "aymurai.llm_providers.ollama_provider.AsyncClient", - return_value=mock_client, - ): - response = await provider.async_generate("Hola") - - self.assertEqual(response.text, "respuesta") - mock_client.chat.assert_awaited_once() - _, kwargs = mock_client.chat.call_args - self.assertEqual(kwargs["model"], "llama3") - self.assertEqual(kwargs["messages"][0]["role"], "system") - self.assertEqual(response.metadata.get("model"), "llama3") - self.assertEqual(response.metadata.get("provider"), "ollama") - self.assertIn("eval_count", response.metadata) - - async def test_async_stream_yields_chunks(self): - provider = OllamaLLMProvider(model="llama3") - - async def fake_gen(): - yield {"message": {"content": "Hola"}, "done": False} - yield {"message": {"content": " Mundo"}, "done": True} - - mock_client = MagicMock() - mock_client.chat = AsyncMock(return_value=fake_gen()) - - with patch( - "aymurai.llm_providers.ollama_provider.AsyncClient", - return_value=mock_client, - ): - pieces = [] - last_chunk = None - async for chunk in provider.async_stream("Hola"): - pieces.append(chunk.text) - last_chunk = chunk - - self.assertEqual(pieces, ["Hola", " Mundo"]) - mock_client.chat.assert_awaited() - self.assertTrue(last_chunk.metadata.get("done")) - - -if __name__ == "__main__": - unittest.main() From 6fd575a2b2b5ed5d0ba5044f760f3874a2c15b40 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Fri, 20 Feb 2026 11:02:20 -0300 Subject: [PATCH 062/101] =?UTF-8?q?=F0=9F=90=9B=20Bug=20fixed=20in=20pypro?= =?UTF-8?q?ject.toml=20line=20106=20for=20.venv=20build=20up?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 1 + uv.lock | 754 ------------------------------------------------- 2 files changed, 1 insertion(+), 754 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f858567c..8a94a2b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,6 +92,7 @@ Homepage = "https://www.aymurai.info/" Repository = "https://github.com/aymurAI/backend" Issues = "https://github.com/AymurAI/backend/issues" +[dependency-groups] dev = [ "matplotlib>=3.10.0", "seaborn>=0.13.2", diff --git a/uv.lock b/uv.lock index 2adadb0f..dbcc39c0 100644 --- a/uv.lock +++ b/uv.lock @@ -8,15 +8,6 @@ resolution-markers = [ "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", ] -[[package]] -name = "absl-py" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" }, -] - [[package]] name = "accelerate" version = "1.12.0" @@ -141,24 +132,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] -[[package]] -name = "anthropic" -version = "0.46.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/68/3b4c045edf6dc6933895e8f279cc77c7684874c8aba46a4e6241c8b147cf/anthropic-0.46.0.tar.gz", hash = "sha256:eac3d43271d02321a57c3ca68aca84c3d58873e8e72d1433288adee2d46b745b", size = 202191, upload-time = "2025-02-18T20:35:33.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/6f/346beae0375df5f6907230bc63d557ef5d7659be49250ac5931a758322ae/anthropic-0.46.0-py3-none-any.whl", hash = "sha256:1445ec9be78d2de7ea51b4d5acd3574e414aea97ef903d0ecbb57bec806aaa49", size = 223228, upload-time = "2025-02-18T20:35:28.659Z" }, -] - [[package]] name = "anyio" version = "4.12.1" @@ -299,12 +272,9 @@ dependencies = [ { name = "flair" }, { name = "jiwer" }, { name = "joblib" }, - { name = "langextract", extra = ["openai"] }, - { name = "marker-pdf" }, { name = "more-itertools" }, { name = "numpy" }, { name = "odfpy" }, - { name = "ollama" }, { name = "psutil" }, { name = "pydantic" }, { name = "pydantic-settings" }, @@ -321,7 +291,6 @@ dependencies = [ { name = "sentencepiece" }, { name = "sqlmodel" }, { name = "tenacity" }, - { name = "tiktoken" }, { name = "torch" }, { name = "unidecode" }, { name = "uvicorn" }, @@ -342,10 +311,6 @@ dev = [ { name = "seaborn" }, { name = "streamlit" }, ] -mlops = [ - { name = "boto3" }, - { name = "mlflow" }, -] [package.metadata] requires-dist = [ @@ -360,12 +325,9 @@ requires-dist = [ { name = "flair", specifier = "==0.15.1" }, { name = "jiwer", specifier = "==3.0.5" }, { name = "joblib", specifier = ">=1.4.2" }, - { name = "langextract", extras = ["openai"], specifier = "==1.1.0" }, - { name = "marker-pdf", specifier = "==1.10.1" }, { name = "more-itertools", specifier = ">=10.5.0" }, { name = "numpy", specifier = "<2.0.0" }, { name = "odfpy", specifier = ">=1.4.1" }, - { name = "ollama", specifier = "==0.6.1" }, { name = "psutil", specifier = "==6.1.0" }, { name = "pydantic", specifier = ">=2.10.4" }, { name = "pydantic-settings", specifier = ">=2.7.0" }, @@ -382,7 +344,6 @@ requires-dist = [ { name = "sentencepiece", specifier = "==0.2.0" }, { name = "sqlmodel", specifier = "==0.0.22" }, { name = "tenacity", specifier = ">=9.0.0" }, - { name = "tiktoken", specifier = "==0.12.0" }, { name = "torch", specifier = ">=1.13.1" }, { name = "unidecode", specifier = "==1.3.8" }, { name = "uvicorn", specifier = ">=0.34.0" }, @@ -403,10 +364,6 @@ dev = [ { name = "seaborn", specifier = ">=0.13.2" }, { name = "streamlit", specifier = ">=1.21.0" }, ] -mlops = [ - { name = "boto3", specifier = ">=1.34.0" }, - { name = "mlflow", specifier = ">=2.12.1" }, -] [[package]] name = "babel" @@ -659,50 +616,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, ] -[[package]] -name = "cryptography" -version = "46.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, - { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, - { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, - { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, - { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, - { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, - { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, - { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, - { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, - { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, - { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, - { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, - { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, - { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, - { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, - { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, - { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, - { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, - { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163, upload-time = "2025-10-15T23:18:13.821Z" }, - { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474, upload-time = "2025-10-15T23:18:15.477Z" }, -] - [[package]] name = "cycler" version = "0.12.1" @@ -712,20 +625,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] -[[package]] -name = "databricks-sdk" -version = "0.77.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "protobuf" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/54/f2393af72974d8e70bfd44b52f17b917190b4934bab66508f4afec9e9285/databricks_sdk-0.77.0.tar.gz", hash = "sha256:56b6017ae17dc31ee821e49746d2ff627a029127166c702088428527813422bd", size = 827462, upload-time = "2026-01-06T13:19:09.432Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/aa/d9186684dbcd338cf51e93621bcf53efa843fe647c0f29f549ebddbf2870/databricks_sdk-0.77.0-py3-none-any.whl", hash = "sha256:42e3211b7dbd53b81a795981149ee08d5b025442a73e464264569edc8c46ee0a", size = 779180, upload-time = "2026-01-06T13:19:07.757Z" }, -] - [[package]] name = "datasets" version = "4.4.2" @@ -844,20 +743,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] -[[package]] -name = "docker" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "requests" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, -] - [[package]] name = "docopt" version = "0.6.2" @@ -902,15 +787,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ca/34/64e900657cad3c4df8a417fccbc4370ca851864edbd0f468210f1b57d084/dspy-3.1.0-py3-none-any.whl", hash = "sha256:b9f4d42cad9ac32b13fdf085dd3246c8ee8339c759cb291c5e7b7f9f5fb5dd72", size = 291317, upload-time = "2026-01-06T18:50:17.559Z" }, ] -[[package]] -name = "einops" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805, upload-time = "2025-02-09T03:17:00.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359, upload-time = "2025-02-09T03:17:01.998Z" }, -] - [[package]] name = "email-validator" version = "2.3.0" @@ -1095,15 +971,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" }, ] -[[package]] -name = "filetype" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, -] - [[package]] name = "fire" version = "0.7.1" @@ -1152,36 +1019,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604, upload-time = "2025-02-05T14:45:41.788Z" }, ] -[[package]] -name = "flask" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "blinker" }, - { name = "click" }, - { name = "itsdangerous" }, - { name = "jinja2" }, - { name = "markupsafe" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/6d/cfe3c0fcc5e477df242b98bfe186a4c34357b4847e87ecaef04507332dab/flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", size = 720160, upload-time = "2025-08-19T21:03:21.205Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308, upload-time = "2025-08-19T21:03:19.499Z" }, -] - -[[package]] -name = "flask-cors" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flask" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472, upload-time = "2025-12-12T20:31:42.861Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257, upload-time = "2025-12-12T20:31:41.3Z" }, -] - [[package]] name = "fonttools" version = "4.61.1" @@ -1307,164 +1144,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, ] -[[package]] -name = "google-api-core" -version = "2.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "googleapis-common-protos" }, - { name = "proto-plus" }, - { name = "protobuf" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759, upload-time = "2025-10-28T21:34:51.529Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706, upload-time = "2025-10-28T21:34:50.151Z" }, -] - -[[package]] -name = "google-auth" -version = "2.47.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1-modules" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/3c/ec64b9a275ca22fa1cd3b6e77fefcf837b0732c890aa32d2bd21313d9b33/google_auth-2.47.0.tar.gz", hash = "sha256:833229070a9dfee1a353ae9877dcd2dec069a8281a4e72e72f77d4a70ff945da", size = 323719, upload-time = "2026-01-06T21:55:31.045Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/18/79e9008530b79527e0d5f79e7eef08d3b179b7f851cfd3a2f27822fbdfa9/google_auth-2.47.0-py3-none-any.whl", hash = "sha256:c516d68336bfde7cf0da26aab674a36fedcf04b37ac4edd59c597178760c3498", size = 234867, upload-time = "2026-01-06T21:55:28.6Z" }, -] - -[package.optional-dependencies] -requests = [ - { name = "requests" }, -] - -[[package]] -name = "google-cloud-core" -version = "2.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "google-auth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027, upload-time = "2025-10-29T23:17:39.513Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469, upload-time = "2025-10-29T23:17:38.548Z" }, -] - -[[package]] -name = "google-cloud-storage" -version = "3.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "google-auth" }, - { name = "google-cloud-core" }, - { name = "google-crc32c" }, - { name = "google-resumable-media" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/8e/fab2de1a0ab7fdbd452eaae5a9a5c933d0911c26b04efa0c76ddfd921259/google_cloud_storage-3.7.0.tar.gz", hash = "sha256:9ce59c65f4d6e372effcecc0456680a8d73cef4f2dc9212a0704799cb3d69237", size = 17258914, upload-time = "2025-12-09T18:24:48.97Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/80/6e5c7c83cea15ed4dfc4843b9df9db0716bc551ac938f7b5dd18a72bd5e4/google_cloud_storage-3.7.0-py3-none-any.whl", hash = "sha256:469bc9540936e02f8a4bfd1619e9dca1e42dec48f95e4204d783b36476a15093", size = 303364, upload-time = "2025-12-09T18:24:47.343Z" }, -] - -[[package]] -name = "google-crc32c" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192, upload-time = "2025-12-16T00:35:25.142Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/ac/6f7bc93886a823ab545948c2dd48143027b2355ad1944c7cf852b338dc91/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff", size = 31296, upload-time = "2025-12-16T00:19:07.261Z" }, - { url = "https://files.pythonhosted.org/packages/f7/97/a5accde175dee985311d949cfcb1249dcbb290f5ec83c994ea733311948f/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288", size = 30870, upload-time = "2025-12-16T00:29:17.669Z" }, - { url = "https://files.pythonhosted.org/packages/3d/63/bec827e70b7a0d4094e7476f863c0dbd6b5f0f1f91d9c9b32b76dcdfeb4e/google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d", size = 33214, upload-time = "2025-12-16T00:40:19.618Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/11b70614df04c289128d782efc084b9035ef8466b3d0a8757c1b6f5cf7ac/google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092", size = 33589, upload-time = "2025-12-16T00:40:20.7Z" }, - { url = "https://files.pythonhosted.org/packages/3e/00/a08a4bc24f1261cc5b0f47312d8aebfbe4b53c2e6307f1b595605eed246b/google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733", size = 34437, upload-time = "2025-12-16T00:35:19.437Z" }, -] - -[[package]] -name = "google-genai" -version = "1.56.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "google-auth", extra = ["requests"] }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "sniffio" }, - { name = "tenacity" }, - { name = "typing-extensions" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/ad/d3ac5a102135bd3f1e4b1475ca65d2bd4bcc22eb2e9348ac40fe3fadb1d6/google_genai-1.56.0.tar.gz", hash = "sha256:0491af33c375f099777ae207d9621f044e27091fafad4c50e617eba32165e82f", size = 340451, upload-time = "2025-12-17T12:35:05.412Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/93/94bc7a89ef4e7ed3666add55cd859d1483a22737251df659bf1aa46e9405/google_genai-1.56.0-py3-none-any.whl", hash = "sha256:9e6b11e0c105ead229368cb5849a480e4d0185519f8d9f538d61ecfcf193b052", size = 426563, upload-time = "2025-12-17T12:35:03.717Z" }, -] - -[[package]] -name = "google-resumable-media" -version = "2.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-crc32c" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265, upload-time = "2025-11-17T15:38:06.659Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340, upload-time = "2025-11-17T15:38:05.594Z" }, -] - -[[package]] -name = "googleapis-common-protos" -version = "1.72.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, -] - -[[package]] -name = "graphene" -version = "3.4.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "graphql-core" }, - { name = "graphql-relay" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739, upload-time = "2024-11-09T20:44:25.757Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894, upload-time = "2024-11-09T20:44:23.851Z" }, -] - -[[package]] -name = "graphql-core" -version = "3.2.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ac/9b/037a640a2983b09aed4a823f9cf1729e6d780b0671f854efa4727a7affbe/graphql_core-3.2.7.tar.gz", hash = "sha256:27b6904bdd3b43f2a0556dad5d579bdfdeab1f38e8e8788e555bdcb586a6f62c", size = 513484, upload-time = "2025-11-01T22:30:40.436Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/14/933037032608787fb92e365883ad6a741c235e0ff992865ec5d904a38f1e/graphql_core-3.2.7-py3-none-any.whl", hash = "sha256:17fc8f3ca4a42913d8e24d9ac9f08deddf0a0b2483076575757f6c412ead2ec0", size = 207262, upload-time = "2025-11-01T22:30:38.912Z" }, -] - -[[package]] -name = "graphql-relay" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "graphql-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/13/98fbf8d67552f102488ffc16c6f559ce71ea15f6294728d33928ab5ff14d/graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c", size = 50027, upload-time = "2022-04-16T11:03:45.447Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/16/a4cf06adbc711bd364a73ce043b0b08d8fa5aae3df11b6ee4248bcdad2e0/graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5", size = 16940, upload-time = "2022-04-16T11:03:43.895Z" }, -] - [[package]] name = "greenlet" version = "3.3.0" @@ -1481,18 +1160,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740, upload-time = "2025-12-04T14:47:52.773Z" }, ] -[[package]] -name = "gunicorn" -version = "23.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, -] - [[package]] name = "h11" version = "0.16.0" @@ -1560,15 +1227,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] -[[package]] -name = "huey" -version = "2.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/29/3428d52eb8e85025e264a291641a9f9d6407cc1e51d1b630f6ac5815999a/huey-2.6.0.tar.gz", hash = "sha256:8d11f8688999d65266af1425b831f6e3773e99415027177b8734b0ffd5e251f6", size = 221068, upload-time = "2026-01-06T03:01:02.055Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/34/fae9ac8f1c3a552fd3f7ff652b94c78d219dedc5fce0c0a4232457760a00/huey-2.6.0-py3-none-any.whl", hash = "sha256:1b9df9d370b49c6d5721ba8a01ac9a787cf86b3bdc584e4679de27b920395c3f", size = 76951, upload-time = "2026-01-06T03:01:00.808Z" }, -] - [[package]] name = "huggingface-hub" version = "0.36.0" @@ -1704,15 +1362,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, ] -[[package]] -name = "itsdangerous" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, -] - [[package]] name = "jedi" version = "0.19.2" @@ -2102,38 +1751,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474, upload-time = "2021-05-07T07:54:13.562Z" } -[[package]] -name = "langextract" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "aiohttp" }, - { name = "async-timeout" }, - { name = "exceptiongroup" }, - { name = "google-cloud-storage" }, - { name = "google-genai" }, - { name = "ml-collections" }, - { name = "more-itertools" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/e8/1a403e454301b92ccdb8a0c2f3a7b5c13e378c35acea00fdf78654539b9f/langextract-1.1.0.tar.gz", hash = "sha256:856672a8d8b0184d7154263e8d046045c822c777ebc23cb1922d580814e2b21f", size = 112029, upload-time = "2025-11-14T22:21:30.511Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/21/4b9eafeb9046514ea36901139137f53fe40e8c5dfc29e1183f4c6b85b890/langextract-1.1.0-py3-none-any.whl", hash = "sha256:e4fae2d9c9134cd0b50602f4714a5dce4c97da5b417fa62f5e602fc066fc1bff", size = 121592, upload-time = "2025-11-14T22:21:29.044Z" }, -] - -[package.optional-dependencies] -openai = [ - { name = "openai" }, -] - [[package]] name = "lark" version = "1.3.1" @@ -2232,60 +1849,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] -[[package]] -name = "markdown2" -version = "2.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652, upload-time = "2025-07-27T16:16:24.307Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954, upload-time = "2025-07-27T16:16:23.026Z" }, -] - -[[package]] -name = "markdownify" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816, upload-time = "2025-11-16T19:21:18.565Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724, upload-time = "2025-11-16T19:21:17.622Z" }, -] - -[[package]] -name = "marker-pdf" -version = "1.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anthropic" }, - { name = "click" }, - { name = "filetype" }, - { name = "ftfy" }, - { name = "google-genai" }, - { name = "markdown2" }, - { name = "markdownify" }, - { name = "openai" }, - { name = "pdftext" }, - { name = "pillow" }, - { name = "pre-commit" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-dotenv" }, - { name = "rapidfuzz" }, - { name = "regex" }, - { name = "scikit-learn" }, - { name = "surya-ocr" }, - { name = "torch" }, - { name = "tqdm" }, - { name = "transformers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/03/708eda637267b6e559ee8c62ff00585ecc78b2700ced4c849bde364409b7/marker_pdf-1.10.1.tar.gz", hash = "sha256:8164bcc04a3b58d6be512cc43571802a00dafc3f799b29207ec9f1edf6092cf9", size = 133302, upload-time = "2025-09-30T15:44:59.591Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/ca/d025e1b10b028e75c22ab2ea5b638ac25b3e121561896000683affbda505/marker_pdf-1.10.1-py3-none-any.whl", hash = "sha256:d7b3beee48216c1e7d5cee888735af1798758049e8f46fc531e899e03f3a1b67", size = 188914, upload-time = "2025-09-30T15:44:58.441Z" }, -] - [[package]] name = "markupsafe" version = "3.0.3" @@ -2366,97 +1929,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" }, ] -[[package]] -name = "ml-collections" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b8/f8/1a9ae6696dbb6bc9c44ddf5c5e84710d77fe9a35a57e8a06722e1836a4a6/ml_collections-1.1.0.tar.gz", hash = "sha256:0ac1ac6511b9f1566863e0bb0afad0c64e906ea278ad3f4d2144a55322671f6f", size = 61356, upload-time = "2025-04-17T08:25:02.247Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/8a/18d4ff2c7bd83f30d6924bd4ad97abf418488c3f908dea228d6f0961ad68/ml_collections-1.1.0-py3-none-any.whl", hash = "sha256:23b6fa4772aac1ae745a96044b925a5746145a70734f087eaca6626e92c05cbc", size = 76707, upload-time = "2025-04-17T08:24:59.038Z" }, -] - -[[package]] -name = "mlflow" -version = "3.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alembic" }, - { name = "cryptography" }, - { name = "docker" }, - { name = "flask" }, - { name = "flask-cors" }, - { name = "graphene" }, - { name = "gunicorn", marker = "sys_platform != 'win32'" }, - { name = "huey" }, - { name = "matplotlib" }, - { name = "mlflow-skinny" }, - { name = "mlflow-tracing" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "pyarrow" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "sqlalchemy" }, - { name = "waitress", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/f8/10ccb111ed53732dfceae0369073023f96acd6b00f92fb3c24473938702d/mlflow-3.8.1.tar.gz", hash = "sha256:0823377bedff4d530b0d560bf394daf9f7e9fbba53453add04eadad34de962cc", size = 8550037, upload-time = "2025-12-26T16:46:49.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/d5/a20b87c6cd99395fee04d6034686512305530c71ceaabe3a151eeaa25ed7/mlflow-3.8.1-py3-none-any.whl", hash = "sha256:42f26b52438fdb615588e150407c6516d0f64d417436dfc75599c525a464f210", size = 9062281, upload-time = "2025-12-26T16:46:46.528Z" }, -] - -[[package]] -name = "mlflow-skinny" -version = "3.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "click" }, - { name = "cloudpickle" }, - { name = "databricks-sdk" }, - { name = "fastapi" }, - { name = "gitpython" }, - { name = "importlib-metadata" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "sqlparse" }, - { name = "typing-extensions" }, - { name = "uvicorn" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/00/18486d9072739e63471c1e441e78cdb6a10c641312d98f6699715406451e/mlflow_skinny-3.8.1.tar.gz", hash = "sha256:0c0aade08187030a4653e267bcd63de2f12cbfebf4c6737832cba45d6fb3594d", size = 2082226, upload-time = "2025-12-26T16:30:11.171Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/24/42e52320636fcbabeaf50704f9269a328acc995e1b8a44df6fea33130a0a/mlflow_skinny-3.8.1-py3-none-any.whl", hash = "sha256:3a6ee27f5ac1e67c1d565fa0e12c070b27129b03e669dcaf88ff841176429142", size = 2506002, upload-time = "2025-12-26T16:30:09.357Z" }, -] - -[[package]] -name = "mlflow-tracing" -version = "3.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "databricks-sdk" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/e5/9cdca5a91afd71e15943f3dbe00e788bd36479637e9b2e0625224027ff01/mlflow_tracing-3.8.1.tar.gz", hash = "sha256:c032ba715994a4580323f3045fa2700a6323033d87e564bbcbda37e6ab993071", size = 1130700, upload-time = "2025-12-26T16:31:39.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/e3/ddbe3e2d1219fa9235559ab88ebf98e1e4f48c62672f0dfb8f1eb07276dc/mlflow_tracing-3.8.1-py3-none-any.whl", hash = "sha256:12d9b5b7177b4152979d003e0d967b280c4252758639aebdfd672734283b17bf", size = 1359007, upload-time = "2025-12-26T16:31:37.514Z" }, -] - [[package]] name = "more-itertools" version = "10.8.0" @@ -2825,19 +2297,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045, upload-time = "2020-01-18T16:55:48.852Z" } -[[package]] -name = "ollama" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620, upload-time = "2025-11-13T23:02:17.416Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354, upload-time = "2025-11-13T23:02:16.292Z" }, -] - [[package]] name = "openai" version = "1.109.1" @@ -2857,75 +2316,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627, upload-time = "2025-09-24T13:00:50.754Z" }, ] -[[package]] -name = "opencv-python-headless" -version = "4.11.0.86" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload-time = "2025-01-16T13:52:57.015Z" }, - { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload-time = "2025-01-16T13:55:45.731Z" }, - { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload-time = "2025-01-16T13:51:59.625Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload-time = "2025-01-16T13:53:29.654Z" }, - { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload-time = "2025-01-16T13:52:49.048Z" }, - { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload-time = "2025-01-16T13:52:56.418Z" }, -] - -[[package]] -name = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - [[package]] name = "optuna" version = "4.6.0" @@ -3022,21 +2412,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, ] -[[package]] -name = "pdftext" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pypdfium2" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/7b/fe3205d44d6058932bbc785f0b9da2ed35b62e17479a8a7d2baca9df1cc6/pdftext-0.6.3.tar.gz", hash = "sha256:ab5c5dfe0f1fb78de1db837ccadac1ea41b07ce1890fead973c9a84cdaf54dec", size = 21968, upload-time = "2025-06-11T14:42:09.492Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/b9/4437bb89f04e57f48c96492a50d6168da5e201940de6620730d390449991/pdftext-0.6.3-py3-none-any.whl", hash = "sha256:528431ed8bdce39d74372cd3d27e8544af812f1f1adc81db229cf9fb48dacacb", size = 23693, upload-time = "2025-06-11T14:42:08.157Z" }, -] - [[package]] name = "pexpect" version = "4.9.0" @@ -3192,18 +2567,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] -[[package]] -name = "proto-plus" -version = "1.27.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/89/9cbe2f4bba860e149108b683bc2efec21f14d5f7ed6e25562ad86acbc373/proto_plus-1.27.0.tar.gz", hash = "sha256:873af56dd0d7e91836aee871e5799e1c6f1bda86ac9a983e0bb9f0c266a568c4", size = 56158, upload-time = "2025-12-16T13:46:25.729Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/24/3b7a0818484df9c28172857af32c2397b6d8fcd99d9468bd4684f98ebf0a/proto_plus-1.27.0-py3-none-any.whl", hash = "sha256:1baa7f81cf0f8acb8bc1f6d085008ba4171eaf669629d1b6d1673b21ed1c0a82", size = 50205, upload-time = "2025-12-16T13:46:24.76Z" }, -] - [[package]] name = "protobuf" version = "6.33.2" @@ -3267,27 +2630,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641, upload-time = "2025-10-24T10:04:22.57Z" }, ] -[[package]] -name = "pyasn1" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, -] - -[[package]] -name = "pyasn1-modules" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, -] - [[package]] name = "pycparser" version = "2.23" @@ -3456,26 +2798,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-py3-none-any.whl", hash = "sha256:023b5e7e5520ad96642e2c6db4cb683d3970bd640cdf7115049a6e9c3682df82", size = 121793, upload-time = "2025-12-23T03:14:02.103Z" }, ] -[[package]] -name = "pypdfium2" -version = "4.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239, upload-time = "2024-05-09T18:33:17.552Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254, upload-time = "2024-05-09T18:32:48.653Z" }, - { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624, upload-time = "2024-05-09T18:32:51.458Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126, upload-time = "2024-05-09T18:32:53.581Z" }, - { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077, upload-time = "2024-05-09T18:32:55.99Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431, upload-time = "2024-05-09T18:32:57.911Z" }, - { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008, upload-time = "2024-05-09T18:32:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543, upload-time = "2024-05-09T18:33:02.597Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911, upload-time = "2024-05-09T18:33:05.376Z" }, - { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430, upload-time = "2024-05-09T18:33:08.067Z" }, - { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951, upload-time = "2024-05-09T18:33:10.567Z" }, - { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098, upload-time = "2024-05-09T18:33:13.107Z" }, - { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118, upload-time = "2024-05-09T18:33:15.489Z" }, -] - [[package]] name = "pysocks" version = "1.7.1" @@ -3580,16 +2902,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, -] - [[package]] name = "pywinpty" version = "3.0.2" @@ -3845,18 +3157,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, ] -[[package]] -name = "rsa" -version = "4.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - [[package]] name = "s3transfer" version = "0.16.0" @@ -4117,15 +3417,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276, upload-time = "2024-08-31T09:43:22.358Z" }, ] -[[package]] -name = "sqlparse" -version = "0.5.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, -] - [[package]] name = "stack-data" version = "0.6.3" @@ -4182,30 +3473,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/95/6b7873f0267973ebd55ba9cd33a690b35a116f2779901ef6185a0e21864d/streamlit-1.52.2-py3-none-any.whl", hash = "sha256:a16bb4fbc9781e173ce9dfbd8ffb189c174f148f9ca4fb8fa56423e84e193fc8", size = 9025937, upload-time = "2025-12-17T17:07:57.67Z" }, ] -[[package]] -name = "surya-ocr" -version = "0.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "einops" }, - { name = "filetype" }, - { name = "opencv-python-headless" }, - { name = "pillow" }, - { name = "platformdirs" }, - { name = "pre-commit" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pypdfium2" }, - { name = "python-dotenv" }, - { name = "torch" }, - { name = "transformers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/97/f868c1034da3d5788eb0d59f4b314f71bafe491e2524d3de3aa42fac2fd4/surya_ocr-0.17.0.tar.gz", hash = "sha256:3110ec9a2be0d4296968ced02ee4d33941f34c145a2d6ac508f75122014ed170", size = 155481, upload-time = "2025-09-23T21:41:37.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/91/7df8763a2d38ce628c3244520e338619b84aedc83ca760e0a0d42c5cf25e/surya_ocr-0.17.0-py3-none-any.whl", hash = "sha256:a728adb1aadd26493f1b937ec411f4b041fa93c8e3524c42b4c627c2e4744d5c", size = 183395, upload-time = "2025-09-23T21:41:35.369Z" }, -] - [[package]] name = "sympy" version = "1.14.0" @@ -4628,15 +3895,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, ] -[[package]] -name = "waitress" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901, upload-time = "2024-11-16T20:02:35.195Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232, upload-time = "2024-11-16T20:02:33.858Z" }, -] - [[package]] name = "watchdog" version = "6.0.0" @@ -4744,18 +4002,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] -[[package]] -name = "werkzeug" -version = "3.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/ea/b0f8eeb287f8df9066e56e831c7824ac6bab645dd6c7a8f4b2d767944f9b/werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e", size = 864687, upload-time = "2025-11-29T02:15:22.841Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/f9/9e082990c2585c744734f85bec79b5dae5df9c974ffee58fe421652c8e91/werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905", size = 224960, upload-time = "2025-11-29T02:15:21.13Z" }, -] - [[package]] name = "widgetsnbextension" version = "4.0.15" From ef1b7f53e564a74cc1dde5863dce222747879956 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Fri, 20 Feb 2026 11:07:31 -0300 Subject: [PATCH 063/101] =?UTF-8?q?=F0=9F=90=9B=20Bug=20fixed=20in=20funct?= =?UTF-8?q?ion=20'=5Fnormalize=5Ftext'=20from=20'aymurai.text.extractors.u?= =?UTF-8?q?tils'=20that=20was=20changed=20to=20'normalize=5Ftext'=20becaus?= =?UTF-8?q?e=20it's=20used=20in=20aymurai/text/extractors/docx.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/text/extractors/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aymurai/text/extractors/utils.py b/aymurai/text/extractors/utils.py index 9c449fe0..881b0633 100644 --- a/aymurai/text/extractors/utils.py +++ b/aymurai/text/extractors/utils.py @@ -24,7 +24,7 @@ ODT_NS = {"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0"} -def _normalize_text(text: str) -> str: +def normalize_text(text: str) -> str: """ Normalize Unicode output consistently across extractors. @@ -140,7 +140,7 @@ def pdf_to_text( paragraphs = _extract_and_merge_paragraphs(file_path, np.ceil(y_tolerance)) docu = "\n\n".join(paragraphs) - return _normalize_text(docu) + return normalize_text(docu) def load_xml_from_docx(path: Path, xmlfile: str = "word/footnotes.xml") -> Any | None: From 90db8918dae1cd7826c970c950d535d6fbf7907d Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Fri, 20 Feb 2026 11:18:49 -0300 Subject: [PATCH 064/101] =?UTF-8?q?=E2=8F=AA=20Revert=20elimination=20of?= =?UTF-8?q?=20folder=20aymurai/experiments/entity=5Fdisambiguation=20for?= =?UTF-8?q?=20experimental=20purposes.=20There=20was=20an=20error=20in=20d?= =?UTF-8?q?eleting=20everything,=20files=20will=20be=20changed=20in=20next?= =?UTF-8?q?=20commit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity_disambiguation/__init__.py | 1 + .../entity_disambiguation/config.py | 176 +++++++ .../entity_disambiguation/manifest.py | 51 ++ .../entity_disambiguation/mlflow_logging.py | 111 +++++ .../entity_disambiguation/runner.py | 468 ++++++++++++++++++ 5 files changed, 807 insertions(+) create mode 100644 aymurai/experiments/entity_disambiguation/__init__.py create mode 100644 aymurai/experiments/entity_disambiguation/config.py create mode 100644 aymurai/experiments/entity_disambiguation/manifest.py create mode 100644 aymurai/experiments/entity_disambiguation/mlflow_logging.py create mode 100644 aymurai/experiments/entity_disambiguation/runner.py diff --git a/aymurai/experiments/entity_disambiguation/__init__.py b/aymurai/experiments/entity_disambiguation/__init__.py new file mode 100644 index 00000000..17dc9d09 --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/__init__.py @@ -0,0 +1 @@ +"""Entity disambiguation experiment helpers.""" diff --git a/aymurai/experiments/entity_disambiguation/config.py b/aymurai/experiments/entity_disambiguation/config.py new file mode 100644 index 00000000..68f54534 --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/config.py @@ -0,0 +1,176 @@ +import os +from datetime import datetime, timezone +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from aymurai.utils.yaml_data import load_yaml + + +class ExperimentConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + name: str + run_name: str + + +class ModelConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + provider: str + name: str + temperature: float | None = None + max_tokens: int | None = None + + +class PromptSpec(BaseModel): + model_config = ConfigDict(extra="forbid") + + id: str + text: str | None = None + + +class PromptsConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + system: PromptSpec + user: PromptSpec + + +class DataManifestConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + enabled: bool = True + mode: Literal["metadata_only"] = "metadata_only" + + +class DataConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + ground_truth_dir: str + input_dir: str + dataset_id: str | None = None + manifest: DataManifestConfig = DataManifestConfig() + + +class PredictionsConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + output_base_dir: str + dir_name_template: str + + +class MLflowConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + tracking_uri: str + experiment_name: str + + +class LoggingPrivacyConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + log_prompt_text: bool = True + log_data_manifest: bool = False + log_per_doc_scores: bool = True + + +class PerDocScoresConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + id_strategy: str = "anon_filename" + + +class LoggingConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + mlflow: MLflowConfig + privacy: LoggingPrivacyConfig = LoggingPrivacyConfig() + per_doc_scores: PerDocScoresConfig = PerDocScoresConfig() + + +class EvaluationConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + function: str = "evaluate_disambiguation" + weights: dict[str, float] = Field( + default_factory=lambda: { + "w_ent": 0.4, + "w_alias": 0.35, + "w_label": 0.2, + "w_role": 0.05, + } + ) + per_doc_scores: bool = True + sim_threshold: float | None = None + normalize: bool = True + + +class ExperimentRunConfig(BaseModel): + model_config = ConfigDict(extra="forbid") + + experiment: ExperimentConfig + model: ModelConfig + prompts: PromptsConfig + data: DataConfig + predictions: PredictionsConfig + logging: LoggingConfig + evaluation: EvaluationConfig + + +def load_experiment_config(path: str) -> ExperimentRunConfig: + """ + Load an experiment configuration from a YAML file. + + Args: + path (str): Path to the YAML configuration file. + + Raises: + ValueError: If the MLflow tracking URI is not set in the environment or configuration. + + Returns: + ExperimentRunConfig: The loaded experiment configuration. + """ + data = load_yaml(path) + tracking_uri = os.getenv("MLFLOW_TRACKING_URI") + if tracking_uri: + logging_cfg = data.setdefault("logging", {}) + mlflow_cfg = logging_cfg.setdefault("mlflow", {}) + mlflow_cfg["tracking_uri"] = tracking_uri + else: + mlflow_cfg = data.get("logging", {}).get("mlflow", {}) + if "tracking_uri" not in mlflow_cfg: + raise ValueError( + "MLFLOW_TRACKING_URI is required but was not set in the environment." + ) + return ExperimentRunConfig.model_validate(data) + + +def render_run_name( + template: str, + model_name: str, + system_id: str, + user_id: str, + *, + timestamp: str | None = None, +) -> str: + """ + Render a run name based on a template and provided identifiers. + + Args: + template (str): The template string for the run name. + model_name (str): The name of the model. + system_id (str): The system identifier. + user_id (str): The user identifier. + timestamp (str | None, optional): The timestamp string. Defaults to None. + + Returns: + str: The rendered run name. + """ + timestamp = timestamp or datetime.now(timezone.utc).strftime("%y%m%d_%H%M") + return template.format( + model=model_name, + system_id=system_id, + user_id=user_id, + timestamp=timestamp, + ) diff --git a/aymurai/experiments/entity_disambiguation/manifest.py b/aymurai/experiments/entity_disambiguation/manifest.py new file mode 100644 index 00000000..2e2c37fa --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/manifest.py @@ -0,0 +1,51 @@ +import hashlib +import json +from dataclasses import dataclass +from pathlib import Path + + +@dataclass(frozen=True) +class DatasetInfo: + dataset_hash: str + dataset_count: int + dataset_bytes_total: int + manifest: dict | None = None + + +def build_metadata_manifest(root_dir: Path) -> DatasetInfo: + """ + Build a metadata manifest for the dataset located at the given root directory. + + Args: + root_dir (Path): The root directory of the dataset. + + Returns: + DatasetInfo: An object containing dataset hash, count, total bytes, and manifest. + """ + files = [] + total_bytes = 0 + + for path in sorted(root_dir.rglob("*")): + if not path.is_file(): + continue + stat = path.stat() + rel_path = path.relative_to(root_dir).as_posix() + files.append( + { + "path": rel_path, + "size_bytes": stat.st_size, + "mtime": int(stat.st_mtime), + } + ) + total_bytes += stat.st_size + + manifest = {"files": files, "summary": {"count": len(files), "bytes": total_bytes}} + manifest_bytes = json.dumps(files, sort_keys=True).encode("utf-8") + dataset_hash = hashlib.sha256(manifest_bytes).hexdigest() + + return DatasetInfo( + dataset_hash=dataset_hash, + dataset_count=len(files), + dataset_bytes_total=total_bytes, + manifest=manifest, + ) diff --git a/aymurai/experiments/entity_disambiguation/mlflow_logging.py b/aymurai/experiments/entity_disambiguation/mlflow_logging.py new file mode 100644 index 00000000..b9e0a799 --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/mlflow_logging.py @@ -0,0 +1,111 @@ +import json +import tempfile +from pathlib import Path + +import mlflow + +from aymurai.experiments.entity_disambiguation.config import ExperimentRunConfig +from aymurai.experiments.entity_disambiguation.manifest import DatasetInfo +from aymurai.utils.yaml_data import save_yaml + + +def configure_mlflow(config: ExperimentRunConfig) -> None: + """ + Configure MLflow tracking URI and experiment name. + + Args: + config (ExperimentRunConfig): Experiment run configuration. + """ + mlflow.set_tracking_uri(config.logging.mlflow.tracking_uri) + mlflow.set_experiment(config.logging.mlflow.experiment_name) + + +def log_run_metadata( + config: ExperimentRunConfig, + *, + run_name: str | None = None, + preds_dir_name: str | None = None, + dataset_info: DatasetInfo | None = None, + metrics: dict[str, float] | None = None, + per_doc_scores: dict[str, object] | None = None, +) -> None: + """ + Log run metadata to MLflow. + + Args: + config (ExperimentRunConfig): Experiment run configuration. + run_name (str | None, optional): Run name. Defaults to None. + preds_dir_name (str | None, optional): Predictions directory name. Defaults to None. + dataset_info (DatasetInfo | None, optional): Dataset information. Defaults to None. + metrics (dict[str, float] | None, optional): Metrics to log. Defaults to None. + per_doc_scores (dict[str, object] | None, optional): Per-document scores to log. Defaults to None. + """ + params = { + "model_provider": config.model.provider, + "model_name": config.model.name, + "system_prompt_id": config.prompts.system.id, + "user_prompt_id": config.prompts.user.id, + "preds_dir_template": config.predictions.dir_name_template, + } + if run_name: + params["run_name"] = run_name + if preds_dir_name: + params["preds_dir_name"] = preds_dir_name + if config.model.temperature is not None: + params["temperature"] = config.model.temperature + if config.model.max_tokens is not None: + params["max_tokens"] = config.model.max_tokens + if config.data.dataset_id: + params["dataset_id"] = config.data.dataset_id + + if dataset_info: + params.update( + { + "dataset_hash": dataset_info.dataset_hash, + "dataset_count": dataset_info.dataset_count, + "dataset_bytes_total": dataset_info.dataset_bytes_total, + } + ) + + mlflow.log_params(params) + + if metrics: + mlflow.log_metrics(metrics) + + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + config_payload = config.model_dump(mode="json") + if not config.logging.privacy.log_prompt_text: + config_payload["prompts"]["system"]["text"] = "" + config_payload["prompts"]["user"]["text"] = "" + config_path = tmp_path / "config.yaml" + save_yaml(config_payload, str(config_path)) + mlflow.log_artifact(str(config_path), artifact_path="config") + + if config.logging.privacy.log_prompt_text: + system_path = tmp_path / "system_prompt.txt" + user_path = tmp_path / "user_prompt.txt" + system_path.write_text(config.prompts.system.text or "", encoding="utf-8") + user_path.write_text(config.prompts.user.text or "", encoding="utf-8") + mlflow.log_artifact(str(system_path), artifact_path="prompts") + mlflow.log_artifact(str(user_path), artifact_path="prompts") + + if ( + config.logging.privacy.log_data_manifest + and dataset_info + and dataset_info.manifest + ): + manifest_path = tmp_path / "manifest.json" + manifest_path.write_text( + json.dumps(dataset_info.manifest, indent=2, sort_keys=True), + encoding="utf-8", + ) + mlflow.log_artifact(str(manifest_path), artifact_path="data") + + if config.logging.privacy.log_per_doc_scores and per_doc_scores: + scores_path = tmp_path / "per_doc_scores.json" + scores_path.write_text( + json.dumps(per_doc_scores, indent=2, sort_keys=True), + encoding="utf-8", + ) + mlflow.log_artifact(str(scores_path), artifact_path="metrics") diff --git a/aymurai/experiments/entity_disambiguation/runner.py b/aymurai/experiments/entity_disambiguation/runner.py new file mode 100644 index 00000000..2cc372ce --- /dev/null +++ b/aymurai/experiments/entity_disambiguation/runner.py @@ -0,0 +1,468 @@ +import argparse +import json +import mimetypes +import os +import re +import time +from pathlib import Path +from typing import Any, Iterable + +import mlflow +import requests +from more_itertools import unique_everseen +from tqdm import tqdm + +from aymurai.evaluation.metrics import evaluate_disambiguation +from aymurai.experiments.entity_disambiguation.config import ( + load_experiment_config, + render_run_name, +) +from aymurai.experiments.entity_disambiguation.manifest import build_metadata_manifest +from aymurai.experiments.entity_disambiguation.mlflow_logging import ( + configure_mlflow, + log_run_metadata, +) +from aymurai.llm_providers import OllamaLLMProvider +from aymurai.logger import get_logger +from aymurai.meta.entities import CanonicalEntities, CanonicalEntity +from aymurai.utils.json_data import get_pretty, load_json, save_json +from aymurai.utils.paths import prediction_filename_for_test, test_id_from_filename + +logger = get_logger(__name__) + +DOC_EXTENSIONS = {".pdf", ".docx"} + + +def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]: + """ + Discover documents in a directory with given extensions. + + Args: + root (Path): Root directory to search for documents. + extensions (Iterable[str]): File extensions to include. + + Returns: + list[Path]: List of discovered document paths with the specified extensions. + """ + extensions = {ext.lower() for ext in extensions} + return sorted( + path + for path in root.rglob("*") + if path.is_file() and path.suffix.lower() in extensions + ) + + +def call_extraction_api( + session: requests.Session, endpoint: str, file_path: Path, timeout_s: float +) -> dict[str, object]: + """ + Call the extraction API with a document file. + + Args: + session (requests.Session): HTTP session for making requests. + endpoint (str): URL of the extraction API endpoint. + file_path (Path): Path to the document file to be processed. + timeout_s (float): Request timeout in seconds. + + Returns: + dict[str, object]: Payload containing the response details. + """ + payload: dict[str, object] = { + "path": str(file_path), + "status": "failure", + "status_code": None, + "elapsed_s": None, + "detail": None, + } + + if not file_path.exists(): + payload["detail"] = "File does not exist" + return payload + + mime_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream" + files = { + "file": (file_path.name, file_path.open("rb"), mime_type), + } + + try: + start = time.perf_counter() + response = session.post( + endpoint, + files=files, + timeout=timeout_s, + ) + elapsed = time.perf_counter() - start + except requests.RequestException as exc: + payload["detail"] = f"Request failed: {exc}" + return payload + finally: + files["file"][1].close() + + payload["status_code"] = response.status_code + payload["elapsed_s"] = elapsed + + try: + response_body = response.json() + except ValueError: + response_body = {"raw": response.text[:500]} + + if response.ok: + payload["status"] = "success" + payload["detail"] = { + "document_id": response_body.get("document_id"), + "document": response_body.get("document", []), + } + else: + payload["detail"] = response_body + + return payload + + +def get_predictions( + session: requests.Session, endpoint: str, sample: str, timeout_s: float +) -> dict: + """ + Get predictions from the prediction API for a given text sample. + + Args: + session (requests.Session): HTTP session for making requests. + endpoint (str): URL of the prediction API endpoint. + sample (str): Text sample to be predicted. + timeout_s (float): Request timeout in seconds. + + Returns: + dict: Prediction response as a dictionary. + """ + response = session.post(url=endpoint, json={"text": sample}, timeout=timeout_s) + response.raise_for_status() + return response.json() + + +def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]: + """ + Parse prediction labels to extract unique aymurai labels and their alternative texts. + + Args: + predictions (list[dict[str, Any]]): List of prediction dictionaries. + + Returns: + list[dict[str, str]]: List of dictionaries containing unique aymurai labels and their alternative texts. + """ + attrs_stream = ( + label.get("attrs") or {} + for label in (label for pred in predictions for label in pred.get("labels", ())) + ) + + unique_pairs = unique_everseen( + ( + attrs.get("aymurai_label"), + attrs.get("aymurai_alt_text"), + ) + for attrs in attrs_stream + if attrs.get("aymurai_label") and attrs.get("aymurai_alt_text") + ) + + return sorted( + ({"aymurai_label": label, "text": text} for label, text in unique_pairs), + key=lambda item: (item["aymurai_label"], item["text"]), + ) + + +def sanitize_filename(path: Path) -> str: + """ + Sanitize a filename by replacing spaces and underscores with hyphens and collapsing multiple hyphens. + + Args: + path (Path): Path object representing the file. + + Returns: + str: Sanitized filename as a string. + """ + base = os.path.splitext(path.name)[0] + target = re.sub(r"\s+|_", "-", base) + target = re.sub(r"-{2,}", "-", target) + return target + + +def extract_canonical_entities( + *, + session: requests.Session, + doc_path: Path, + extract_endpoint: str, + predict_endpoint: str, + timeout_s: float, + model_name: str, + system_prompt: str, + user_prompt_template: str, + temperature: float | None, + max_tokens: int | None, +) -> list[CanonicalEntity]: + """ + Extract canonical entities from a document using extraction and prediction APIs. + + Args: + session (requests.Session): HTTP session for making requests. + doc_path (Path): Path to the document file. + extract_endpoint (str): URL of the extraction API endpoint. + predict_endpoint (str): URL of the prediction API endpoint. + timeout_s (float): Request timeout in seconds. + model_name (str): Name of the LLM model to use. + system_prompt (str): System prompt text. + user_prompt_template (str): User prompt template text. + temperature (float | None): Temperature setting for the LLM. + max_tokens (int | None): Maximum tokens setting for the LLM. + + Raises: + ValueError: If the document text is empty or not found. + + Returns: + list[CanonicalEntity]: List of extracted canonical entities. + """ + document_payload = call_extraction_api( + session, + extract_endpoint, + doc_path, + timeout_s, + ) + document = document_payload.get("detail", {}).get("document") + + if not document: + raise ValueError("Document text is empty or not found.") + + ner_predictions = [ + get_predictions(session, predict_endpoint, paragraph, timeout_s) + for paragraph in tqdm(document, desc=f"NER {doc_path.name}") + ] + parsed_ner_labels = parse_prediction_labels(ner_predictions) + ner_output_json = get_pretty(parsed_ner_labels) + + user_prompt = user_prompt_template.format( + document_text="\n".join(document).strip(), + ner_output_json=ner_output_json, + ) + + provider = OllamaLLMProvider(model=model_name) + options: dict[str, object] = {} + if temperature is not None: + options["temperature"] = temperature + if max_tokens is not None: + options["num_predict"] = max_tokens + + generate_kwargs: dict[str, object] = { + "messages": [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt}, + ], + "format": CanonicalEntities.model_json_schema(), + } + if options: + generate_kwargs["options"] = options + + response = provider.generate(**generate_kwargs) + + payload = json.loads(response.text) + canonical_entities = [ + CanonicalEntity.model_validate(output) + for output in payload.get("canonical_entities", []) + ] + + return canonical_entities + + +def run_experiment(config_path: str) -> None: + """ + Run the entity disambiguation experiment based on the provided configuration. + + Args: + config_path (str): Path to the experiment configuration file. + + Raises: + FileNotFoundError: If the input directory is not found. + FileNotFoundError: If the ground truth directory is not found. + RuntimeError: If no documents are found in the input directory. + ValueError: If there is an error in processing the documents. + """ + config = load_experiment_config(config_path) + + input_dir = Path(config.data.input_dir) + ground_truth_dir = Path(config.data.ground_truth_dir) + + if not input_dir.exists(): + raise FileNotFoundError(f"Input directory not found: {input_dir}") + if not ground_truth_dir.exists(): + raise FileNotFoundError(f"Ground truth directory not found: {ground_truth_dir}") + + documents = discover_documents(input_dir, DOC_EXTENSIONS) + if not documents: + raise RuntimeError(f"No documents found under {input_dir}") + + timestamp = time.strftime("%y%m%d_%H%M") + run_name = render_run_name( + config.experiment.run_name, + config.model.name, + config.prompts.system.id, + config.prompts.user.id, + timestamp=timestamp, + ) + preds_dir_name = render_run_name( + config.predictions.dir_name_template, + config.model.name, + config.prompts.system.id, + config.prompts.user.id, + timestamp=timestamp, + ) + output_dir = Path(config.predictions.output_base_dir) / preds_dir_name + output_dir.mkdir(parents=True, exist_ok=False) + + dataset_info = ( + build_metadata_manifest(ground_truth_dir) + if config.data.manifest.enabled + else None + ) + + api_base_url = os.getenv("API_BASE_URL", "http://localhost:8899") + extract_endpoint = f"{api_base_url}/misc/document-extract" + predict_endpoint = f"{api_base_url}/anonymizer/predict" + timeout_s = float(os.getenv("REQUEST_TIMEOUT", "30")) + + logger.info(f"Processing {len(documents)} documents from {input_dir}") + logger.info(f"Predictions will be stored in {output_dir}") + + configure_mlflow(config) + + per_doc_scores: dict[str, object] = {} + metrics_summary: dict[str, float] = {} + + with mlflow.start_run(run_name=run_name): + session = requests.Session() + + processed = 0 + failed = 0 + + for doc_path in documents: + logger.info(f"Processing document: {doc_path.name}") + try: + canonical_entities = extract_canonical_entities( + session=session, + doc_path=doc_path, + extract_endpoint=extract_endpoint, + predict_endpoint=predict_endpoint, + timeout_s=timeout_s, + model_name=config.model.name, + system_prompt=config.prompts.system.text or "", + user_prompt_template=config.prompts.user.text or "", + temperature=config.model.temperature, + max_tokens=config.model.max_tokens, + ) + target_filename = sanitize_filename(doc_path) + target_path = output_dir / f"{target_filename}.json" + save_json( + [ + entity.model_dump() + | { + "entity_id": entity.entity_id.hex + if entity.entity_id + else None + } + for entity in canonical_entities + ], + str(target_path), + ) + processed += 1 + except Exception as exc: + failed += 1 + logger.warning(f"Error processing {doc_path.name}: {exc}") + + if config.evaluation.function != "evaluate_disambiguation": + raise ValueError( + f"Unsupported evaluation function: {config.evaluation.function}" + ) + + results = [] + if output_dir.exists(): + for test_path in sorted(ground_truth_dir.glob("*.json")): + doc_id = test_id_from_filename(test_path) + pred_path = output_dir / prediction_filename_for_test(test_path) + + if not pred_path.exists(): + logger.warning( + f"Prediction for {test_path.name} was not found; skipping." + ) + continue + + gold_data = load_json(str(test_path)) + pred_data = load_json(str(pred_path)) + + score, metrics = evaluate_disambiguation( + gold_data, + pred_data, + w_ent=config.evaluation.weights.get("w_ent", 0.4), + w_alias=config.evaluation.weights.get("w_alias", 0.35), + w_label=config.evaluation.weights.get("w_label", 0.2), + w_role=config.evaluation.weights.get("w_role", 0.05), + sim_threshold=config.evaluation.sim_threshold or 0.3, + normalize=config.evaluation.normalize, + ) + results.append((doc_id, score, metrics)) + + average = ( + sum(score for _, score, _ in results) / len(results) + if results + else float("nan") + ) + + if results: + metrics_summary["score_avg"] = average + metrics_summary["doc_count"] = float(len(results)) + + metric_names = set(results[0][2].keys()) + for metric_name in metric_names: + metrics_summary[f"{metric_name}_avg"] = sum( + metrics.get(metric_name, 0.0) for _, _, metrics in results + ) / len(results) + + for doc_id, score, metrics in results: + per_doc_scores[doc_id] = {"score": score, **metrics} + + metrics_summary["docs_processed"] = float(processed) + metrics_summary["docs_failed"] = float(failed) + + log_run_metadata( + config, + run_name=run_name, + preds_dir_name=preds_dir_name, + dataset_info=dataset_info, + metrics=metrics_summary if metrics_summary else None, + per_doc_scores=per_doc_scores if per_doc_scores else None, + ) + + +def build_arg_parser() -> argparse.ArgumentParser: + """ + Build the argument parser for the entity disambiguation experiment. + + Returns: + argparse.ArgumentParser: The configured argument parser. + """ + parser = argparse.ArgumentParser( + description="Run entity disambiguation experiment from a YAML config.", + ) + parser.add_argument( + "--config", + required=True, + help="Path to the experiment YAML config.", + ) + return parser + + +def main() -> None: + """ + Main entry point for the entity disambiguation experiment script. + Parses command-line arguments and runs the experiment. + """ + args = build_arg_parser().parse_args() + run_experiment(args.config) + + +if __name__ == "__main__": + main() From 9afde5031d605ec7ae811b08dde8449a466b07f7 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Fri, 20 Feb 2026 11:32:34 -0300 Subject: [PATCH 065/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20aymurai/experi?= =?UTF-8?q?ments/entity=5Fdisambiguation=20for=20release/v1.5.0=20compatib?= =?UTF-8?q?ility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity_disambiguation/__init__.py | 1 - .../entity_disambiguation/config.py | 176 ------- .../entity_disambiguation/manifest.py | 51 -- .../entity_disambiguation/mlflow_logging.py | 111 ----- .../entity_disambiguation/runner.py | 468 ------------------ 5 files changed, 807 deletions(-) delete mode 100644 aymurai/experiments/entity_disambiguation/__init__.py delete mode 100644 aymurai/experiments/entity_disambiguation/config.py delete mode 100644 aymurai/experiments/entity_disambiguation/manifest.py delete mode 100644 aymurai/experiments/entity_disambiguation/mlflow_logging.py delete mode 100644 aymurai/experiments/entity_disambiguation/runner.py diff --git a/aymurai/experiments/entity_disambiguation/__init__.py b/aymurai/experiments/entity_disambiguation/__init__.py deleted file mode 100644 index 17dc9d09..00000000 --- a/aymurai/experiments/entity_disambiguation/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Entity disambiguation experiment helpers.""" diff --git a/aymurai/experiments/entity_disambiguation/config.py b/aymurai/experiments/entity_disambiguation/config.py deleted file mode 100644 index 68f54534..00000000 --- a/aymurai/experiments/entity_disambiguation/config.py +++ /dev/null @@ -1,176 +0,0 @@ -import os -from datetime import datetime, timezone -from typing import Literal - -from pydantic import BaseModel, ConfigDict, Field - -from aymurai.utils.yaml_data import load_yaml - - -class ExperimentConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - name: str - run_name: str - - -class ModelConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - provider: str - name: str - temperature: float | None = None - max_tokens: int | None = None - - -class PromptSpec(BaseModel): - model_config = ConfigDict(extra="forbid") - - id: str - text: str | None = None - - -class PromptsConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - system: PromptSpec - user: PromptSpec - - -class DataManifestConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - enabled: bool = True - mode: Literal["metadata_only"] = "metadata_only" - - -class DataConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - ground_truth_dir: str - input_dir: str - dataset_id: str | None = None - manifest: DataManifestConfig = DataManifestConfig() - - -class PredictionsConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - output_base_dir: str - dir_name_template: str - - -class MLflowConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - tracking_uri: str - experiment_name: str - - -class LoggingPrivacyConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - log_prompt_text: bool = True - log_data_manifest: bool = False - log_per_doc_scores: bool = True - - -class PerDocScoresConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - id_strategy: str = "anon_filename" - - -class LoggingConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - mlflow: MLflowConfig - privacy: LoggingPrivacyConfig = LoggingPrivacyConfig() - per_doc_scores: PerDocScoresConfig = PerDocScoresConfig() - - -class EvaluationConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - function: str = "evaluate_disambiguation" - weights: dict[str, float] = Field( - default_factory=lambda: { - "w_ent": 0.4, - "w_alias": 0.35, - "w_label": 0.2, - "w_role": 0.05, - } - ) - per_doc_scores: bool = True - sim_threshold: float | None = None - normalize: bool = True - - -class ExperimentRunConfig(BaseModel): - model_config = ConfigDict(extra="forbid") - - experiment: ExperimentConfig - model: ModelConfig - prompts: PromptsConfig - data: DataConfig - predictions: PredictionsConfig - logging: LoggingConfig - evaluation: EvaluationConfig - - -def load_experiment_config(path: str) -> ExperimentRunConfig: - """ - Load an experiment configuration from a YAML file. - - Args: - path (str): Path to the YAML configuration file. - - Raises: - ValueError: If the MLflow tracking URI is not set in the environment or configuration. - - Returns: - ExperimentRunConfig: The loaded experiment configuration. - """ - data = load_yaml(path) - tracking_uri = os.getenv("MLFLOW_TRACKING_URI") - if tracking_uri: - logging_cfg = data.setdefault("logging", {}) - mlflow_cfg = logging_cfg.setdefault("mlflow", {}) - mlflow_cfg["tracking_uri"] = tracking_uri - else: - mlflow_cfg = data.get("logging", {}).get("mlflow", {}) - if "tracking_uri" not in mlflow_cfg: - raise ValueError( - "MLFLOW_TRACKING_URI is required but was not set in the environment." - ) - return ExperimentRunConfig.model_validate(data) - - -def render_run_name( - template: str, - model_name: str, - system_id: str, - user_id: str, - *, - timestamp: str | None = None, -) -> str: - """ - Render a run name based on a template and provided identifiers. - - Args: - template (str): The template string for the run name. - model_name (str): The name of the model. - system_id (str): The system identifier. - user_id (str): The user identifier. - timestamp (str | None, optional): The timestamp string. Defaults to None. - - Returns: - str: The rendered run name. - """ - timestamp = timestamp or datetime.now(timezone.utc).strftime("%y%m%d_%H%M") - return template.format( - model=model_name, - system_id=system_id, - user_id=user_id, - timestamp=timestamp, - ) diff --git a/aymurai/experiments/entity_disambiguation/manifest.py b/aymurai/experiments/entity_disambiguation/manifest.py deleted file mode 100644 index 2e2c37fa..00000000 --- a/aymurai/experiments/entity_disambiguation/manifest.py +++ /dev/null @@ -1,51 +0,0 @@ -import hashlib -import json -from dataclasses import dataclass -from pathlib import Path - - -@dataclass(frozen=True) -class DatasetInfo: - dataset_hash: str - dataset_count: int - dataset_bytes_total: int - manifest: dict | None = None - - -def build_metadata_manifest(root_dir: Path) -> DatasetInfo: - """ - Build a metadata manifest for the dataset located at the given root directory. - - Args: - root_dir (Path): The root directory of the dataset. - - Returns: - DatasetInfo: An object containing dataset hash, count, total bytes, and manifest. - """ - files = [] - total_bytes = 0 - - for path in sorted(root_dir.rglob("*")): - if not path.is_file(): - continue - stat = path.stat() - rel_path = path.relative_to(root_dir).as_posix() - files.append( - { - "path": rel_path, - "size_bytes": stat.st_size, - "mtime": int(stat.st_mtime), - } - ) - total_bytes += stat.st_size - - manifest = {"files": files, "summary": {"count": len(files), "bytes": total_bytes}} - manifest_bytes = json.dumps(files, sort_keys=True).encode("utf-8") - dataset_hash = hashlib.sha256(manifest_bytes).hexdigest() - - return DatasetInfo( - dataset_hash=dataset_hash, - dataset_count=len(files), - dataset_bytes_total=total_bytes, - manifest=manifest, - ) diff --git a/aymurai/experiments/entity_disambiguation/mlflow_logging.py b/aymurai/experiments/entity_disambiguation/mlflow_logging.py deleted file mode 100644 index b9e0a799..00000000 --- a/aymurai/experiments/entity_disambiguation/mlflow_logging.py +++ /dev/null @@ -1,111 +0,0 @@ -import json -import tempfile -from pathlib import Path - -import mlflow - -from aymurai.experiments.entity_disambiguation.config import ExperimentRunConfig -from aymurai.experiments.entity_disambiguation.manifest import DatasetInfo -from aymurai.utils.yaml_data import save_yaml - - -def configure_mlflow(config: ExperimentRunConfig) -> None: - """ - Configure MLflow tracking URI and experiment name. - - Args: - config (ExperimentRunConfig): Experiment run configuration. - """ - mlflow.set_tracking_uri(config.logging.mlflow.tracking_uri) - mlflow.set_experiment(config.logging.mlflow.experiment_name) - - -def log_run_metadata( - config: ExperimentRunConfig, - *, - run_name: str | None = None, - preds_dir_name: str | None = None, - dataset_info: DatasetInfo | None = None, - metrics: dict[str, float] | None = None, - per_doc_scores: dict[str, object] | None = None, -) -> None: - """ - Log run metadata to MLflow. - - Args: - config (ExperimentRunConfig): Experiment run configuration. - run_name (str | None, optional): Run name. Defaults to None. - preds_dir_name (str | None, optional): Predictions directory name. Defaults to None. - dataset_info (DatasetInfo | None, optional): Dataset information. Defaults to None. - metrics (dict[str, float] | None, optional): Metrics to log. Defaults to None. - per_doc_scores (dict[str, object] | None, optional): Per-document scores to log. Defaults to None. - """ - params = { - "model_provider": config.model.provider, - "model_name": config.model.name, - "system_prompt_id": config.prompts.system.id, - "user_prompt_id": config.prompts.user.id, - "preds_dir_template": config.predictions.dir_name_template, - } - if run_name: - params["run_name"] = run_name - if preds_dir_name: - params["preds_dir_name"] = preds_dir_name - if config.model.temperature is not None: - params["temperature"] = config.model.temperature - if config.model.max_tokens is not None: - params["max_tokens"] = config.model.max_tokens - if config.data.dataset_id: - params["dataset_id"] = config.data.dataset_id - - if dataset_info: - params.update( - { - "dataset_hash": dataset_info.dataset_hash, - "dataset_count": dataset_info.dataset_count, - "dataset_bytes_total": dataset_info.dataset_bytes_total, - } - ) - - mlflow.log_params(params) - - if metrics: - mlflow.log_metrics(metrics) - - with tempfile.TemporaryDirectory() as tmp_dir: - tmp_path = Path(tmp_dir) - config_payload = config.model_dump(mode="json") - if not config.logging.privacy.log_prompt_text: - config_payload["prompts"]["system"]["text"] = "" - config_payload["prompts"]["user"]["text"] = "" - config_path = tmp_path / "config.yaml" - save_yaml(config_payload, str(config_path)) - mlflow.log_artifact(str(config_path), artifact_path="config") - - if config.logging.privacy.log_prompt_text: - system_path = tmp_path / "system_prompt.txt" - user_path = tmp_path / "user_prompt.txt" - system_path.write_text(config.prompts.system.text or "", encoding="utf-8") - user_path.write_text(config.prompts.user.text or "", encoding="utf-8") - mlflow.log_artifact(str(system_path), artifact_path="prompts") - mlflow.log_artifact(str(user_path), artifact_path="prompts") - - if ( - config.logging.privacy.log_data_manifest - and dataset_info - and dataset_info.manifest - ): - manifest_path = tmp_path / "manifest.json" - manifest_path.write_text( - json.dumps(dataset_info.manifest, indent=2, sort_keys=True), - encoding="utf-8", - ) - mlflow.log_artifact(str(manifest_path), artifact_path="data") - - if config.logging.privacy.log_per_doc_scores and per_doc_scores: - scores_path = tmp_path / "per_doc_scores.json" - scores_path.write_text( - json.dumps(per_doc_scores, indent=2, sort_keys=True), - encoding="utf-8", - ) - mlflow.log_artifact(str(scores_path), artifact_path="metrics") diff --git a/aymurai/experiments/entity_disambiguation/runner.py b/aymurai/experiments/entity_disambiguation/runner.py deleted file mode 100644 index 2cc372ce..00000000 --- a/aymurai/experiments/entity_disambiguation/runner.py +++ /dev/null @@ -1,468 +0,0 @@ -import argparse -import json -import mimetypes -import os -import re -import time -from pathlib import Path -from typing import Any, Iterable - -import mlflow -import requests -from more_itertools import unique_everseen -from tqdm import tqdm - -from aymurai.evaluation.metrics import evaluate_disambiguation -from aymurai.experiments.entity_disambiguation.config import ( - load_experiment_config, - render_run_name, -) -from aymurai.experiments.entity_disambiguation.manifest import build_metadata_manifest -from aymurai.experiments.entity_disambiguation.mlflow_logging import ( - configure_mlflow, - log_run_metadata, -) -from aymurai.llm_providers import OllamaLLMProvider -from aymurai.logger import get_logger -from aymurai.meta.entities import CanonicalEntities, CanonicalEntity -from aymurai.utils.json_data import get_pretty, load_json, save_json -from aymurai.utils.paths import prediction_filename_for_test, test_id_from_filename - -logger = get_logger(__name__) - -DOC_EXTENSIONS = {".pdf", ".docx"} - - -def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]: - """ - Discover documents in a directory with given extensions. - - Args: - root (Path): Root directory to search for documents. - extensions (Iterable[str]): File extensions to include. - - Returns: - list[Path]: List of discovered document paths with the specified extensions. - """ - extensions = {ext.lower() for ext in extensions} - return sorted( - path - for path in root.rglob("*") - if path.is_file() and path.suffix.lower() in extensions - ) - - -def call_extraction_api( - session: requests.Session, endpoint: str, file_path: Path, timeout_s: float -) -> dict[str, object]: - """ - Call the extraction API with a document file. - - Args: - session (requests.Session): HTTP session for making requests. - endpoint (str): URL of the extraction API endpoint. - file_path (Path): Path to the document file to be processed. - timeout_s (float): Request timeout in seconds. - - Returns: - dict[str, object]: Payload containing the response details. - """ - payload: dict[str, object] = { - "path": str(file_path), - "status": "failure", - "status_code": None, - "elapsed_s": None, - "detail": None, - } - - if not file_path.exists(): - payload["detail"] = "File does not exist" - return payload - - mime_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream" - files = { - "file": (file_path.name, file_path.open("rb"), mime_type), - } - - try: - start = time.perf_counter() - response = session.post( - endpoint, - files=files, - timeout=timeout_s, - ) - elapsed = time.perf_counter() - start - except requests.RequestException as exc: - payload["detail"] = f"Request failed: {exc}" - return payload - finally: - files["file"][1].close() - - payload["status_code"] = response.status_code - payload["elapsed_s"] = elapsed - - try: - response_body = response.json() - except ValueError: - response_body = {"raw": response.text[:500]} - - if response.ok: - payload["status"] = "success" - payload["detail"] = { - "document_id": response_body.get("document_id"), - "document": response_body.get("document", []), - } - else: - payload["detail"] = response_body - - return payload - - -def get_predictions( - session: requests.Session, endpoint: str, sample: str, timeout_s: float -) -> dict: - """ - Get predictions from the prediction API for a given text sample. - - Args: - session (requests.Session): HTTP session for making requests. - endpoint (str): URL of the prediction API endpoint. - sample (str): Text sample to be predicted. - timeout_s (float): Request timeout in seconds. - - Returns: - dict: Prediction response as a dictionary. - """ - response = session.post(url=endpoint, json={"text": sample}, timeout=timeout_s) - response.raise_for_status() - return response.json() - - -def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]: - """ - Parse prediction labels to extract unique aymurai labels and their alternative texts. - - Args: - predictions (list[dict[str, Any]]): List of prediction dictionaries. - - Returns: - list[dict[str, str]]: List of dictionaries containing unique aymurai labels and their alternative texts. - """ - attrs_stream = ( - label.get("attrs") or {} - for label in (label for pred in predictions for label in pred.get("labels", ())) - ) - - unique_pairs = unique_everseen( - ( - attrs.get("aymurai_label"), - attrs.get("aymurai_alt_text"), - ) - for attrs in attrs_stream - if attrs.get("aymurai_label") and attrs.get("aymurai_alt_text") - ) - - return sorted( - ({"aymurai_label": label, "text": text} for label, text in unique_pairs), - key=lambda item: (item["aymurai_label"], item["text"]), - ) - - -def sanitize_filename(path: Path) -> str: - """ - Sanitize a filename by replacing spaces and underscores with hyphens and collapsing multiple hyphens. - - Args: - path (Path): Path object representing the file. - - Returns: - str: Sanitized filename as a string. - """ - base = os.path.splitext(path.name)[0] - target = re.sub(r"\s+|_", "-", base) - target = re.sub(r"-{2,}", "-", target) - return target - - -def extract_canonical_entities( - *, - session: requests.Session, - doc_path: Path, - extract_endpoint: str, - predict_endpoint: str, - timeout_s: float, - model_name: str, - system_prompt: str, - user_prompt_template: str, - temperature: float | None, - max_tokens: int | None, -) -> list[CanonicalEntity]: - """ - Extract canonical entities from a document using extraction and prediction APIs. - - Args: - session (requests.Session): HTTP session for making requests. - doc_path (Path): Path to the document file. - extract_endpoint (str): URL of the extraction API endpoint. - predict_endpoint (str): URL of the prediction API endpoint. - timeout_s (float): Request timeout in seconds. - model_name (str): Name of the LLM model to use. - system_prompt (str): System prompt text. - user_prompt_template (str): User prompt template text. - temperature (float | None): Temperature setting for the LLM. - max_tokens (int | None): Maximum tokens setting for the LLM. - - Raises: - ValueError: If the document text is empty or not found. - - Returns: - list[CanonicalEntity]: List of extracted canonical entities. - """ - document_payload = call_extraction_api( - session, - extract_endpoint, - doc_path, - timeout_s, - ) - document = document_payload.get("detail", {}).get("document") - - if not document: - raise ValueError("Document text is empty or not found.") - - ner_predictions = [ - get_predictions(session, predict_endpoint, paragraph, timeout_s) - for paragraph in tqdm(document, desc=f"NER {doc_path.name}") - ] - parsed_ner_labels = parse_prediction_labels(ner_predictions) - ner_output_json = get_pretty(parsed_ner_labels) - - user_prompt = user_prompt_template.format( - document_text="\n".join(document).strip(), - ner_output_json=ner_output_json, - ) - - provider = OllamaLLMProvider(model=model_name) - options: dict[str, object] = {} - if temperature is not None: - options["temperature"] = temperature - if max_tokens is not None: - options["num_predict"] = max_tokens - - generate_kwargs: dict[str, object] = { - "messages": [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_prompt}, - ], - "format": CanonicalEntities.model_json_schema(), - } - if options: - generate_kwargs["options"] = options - - response = provider.generate(**generate_kwargs) - - payload = json.loads(response.text) - canonical_entities = [ - CanonicalEntity.model_validate(output) - for output in payload.get("canonical_entities", []) - ] - - return canonical_entities - - -def run_experiment(config_path: str) -> None: - """ - Run the entity disambiguation experiment based on the provided configuration. - - Args: - config_path (str): Path to the experiment configuration file. - - Raises: - FileNotFoundError: If the input directory is not found. - FileNotFoundError: If the ground truth directory is not found. - RuntimeError: If no documents are found in the input directory. - ValueError: If there is an error in processing the documents. - """ - config = load_experiment_config(config_path) - - input_dir = Path(config.data.input_dir) - ground_truth_dir = Path(config.data.ground_truth_dir) - - if not input_dir.exists(): - raise FileNotFoundError(f"Input directory not found: {input_dir}") - if not ground_truth_dir.exists(): - raise FileNotFoundError(f"Ground truth directory not found: {ground_truth_dir}") - - documents = discover_documents(input_dir, DOC_EXTENSIONS) - if not documents: - raise RuntimeError(f"No documents found under {input_dir}") - - timestamp = time.strftime("%y%m%d_%H%M") - run_name = render_run_name( - config.experiment.run_name, - config.model.name, - config.prompts.system.id, - config.prompts.user.id, - timestamp=timestamp, - ) - preds_dir_name = render_run_name( - config.predictions.dir_name_template, - config.model.name, - config.prompts.system.id, - config.prompts.user.id, - timestamp=timestamp, - ) - output_dir = Path(config.predictions.output_base_dir) / preds_dir_name - output_dir.mkdir(parents=True, exist_ok=False) - - dataset_info = ( - build_metadata_manifest(ground_truth_dir) - if config.data.manifest.enabled - else None - ) - - api_base_url = os.getenv("API_BASE_URL", "http://localhost:8899") - extract_endpoint = f"{api_base_url}/misc/document-extract" - predict_endpoint = f"{api_base_url}/anonymizer/predict" - timeout_s = float(os.getenv("REQUEST_TIMEOUT", "30")) - - logger.info(f"Processing {len(documents)} documents from {input_dir}") - logger.info(f"Predictions will be stored in {output_dir}") - - configure_mlflow(config) - - per_doc_scores: dict[str, object] = {} - metrics_summary: dict[str, float] = {} - - with mlflow.start_run(run_name=run_name): - session = requests.Session() - - processed = 0 - failed = 0 - - for doc_path in documents: - logger.info(f"Processing document: {doc_path.name}") - try: - canonical_entities = extract_canonical_entities( - session=session, - doc_path=doc_path, - extract_endpoint=extract_endpoint, - predict_endpoint=predict_endpoint, - timeout_s=timeout_s, - model_name=config.model.name, - system_prompt=config.prompts.system.text or "", - user_prompt_template=config.prompts.user.text or "", - temperature=config.model.temperature, - max_tokens=config.model.max_tokens, - ) - target_filename = sanitize_filename(doc_path) - target_path = output_dir / f"{target_filename}.json" - save_json( - [ - entity.model_dump() - | { - "entity_id": entity.entity_id.hex - if entity.entity_id - else None - } - for entity in canonical_entities - ], - str(target_path), - ) - processed += 1 - except Exception as exc: - failed += 1 - logger.warning(f"Error processing {doc_path.name}: {exc}") - - if config.evaluation.function != "evaluate_disambiguation": - raise ValueError( - f"Unsupported evaluation function: {config.evaluation.function}" - ) - - results = [] - if output_dir.exists(): - for test_path in sorted(ground_truth_dir.glob("*.json")): - doc_id = test_id_from_filename(test_path) - pred_path = output_dir / prediction_filename_for_test(test_path) - - if not pred_path.exists(): - logger.warning( - f"Prediction for {test_path.name} was not found; skipping." - ) - continue - - gold_data = load_json(str(test_path)) - pred_data = load_json(str(pred_path)) - - score, metrics = evaluate_disambiguation( - gold_data, - pred_data, - w_ent=config.evaluation.weights.get("w_ent", 0.4), - w_alias=config.evaluation.weights.get("w_alias", 0.35), - w_label=config.evaluation.weights.get("w_label", 0.2), - w_role=config.evaluation.weights.get("w_role", 0.05), - sim_threshold=config.evaluation.sim_threshold or 0.3, - normalize=config.evaluation.normalize, - ) - results.append((doc_id, score, metrics)) - - average = ( - sum(score for _, score, _ in results) / len(results) - if results - else float("nan") - ) - - if results: - metrics_summary["score_avg"] = average - metrics_summary["doc_count"] = float(len(results)) - - metric_names = set(results[0][2].keys()) - for metric_name in metric_names: - metrics_summary[f"{metric_name}_avg"] = sum( - metrics.get(metric_name, 0.0) for _, _, metrics in results - ) / len(results) - - for doc_id, score, metrics in results: - per_doc_scores[doc_id] = {"score": score, **metrics} - - metrics_summary["docs_processed"] = float(processed) - metrics_summary["docs_failed"] = float(failed) - - log_run_metadata( - config, - run_name=run_name, - preds_dir_name=preds_dir_name, - dataset_info=dataset_info, - metrics=metrics_summary if metrics_summary else None, - per_doc_scores=per_doc_scores if per_doc_scores else None, - ) - - -def build_arg_parser() -> argparse.ArgumentParser: - """ - Build the argument parser for the entity disambiguation experiment. - - Returns: - argparse.ArgumentParser: The configured argument parser. - """ - parser = argparse.ArgumentParser( - description="Run entity disambiguation experiment from a YAML config.", - ) - parser.add_argument( - "--config", - required=True, - help="Path to the experiment YAML config.", - ) - return parser - - -def main() -> None: - """ - Main entry point for the entity disambiguation experiment script. - Parses command-line arguments and runs the experiment. - """ - args = build_arg_parser().parse_args() - run_experiment(args.config) - - -if __name__ == "__main__": - main() From c9e14843b94ff3262a89ed74fabfb1edff39f80a Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Fri, 20 Feb 2026 11:36:20 -0300 Subject: [PATCH 066/101] =?UTF-8?q?=F0=9F=90=9B=20Bug=20fixed=20in=20exper?= =?UTF-8?q?iments/entity-disambiguation/10-anonymize-document-render-polic?= =?UTF-8?q?y.ipynb=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../10-anonymize-document-render-policy.ipynb | 135 +++++++++++++++++- 1 file changed, 130 insertions(+), 5 deletions(-) diff --git a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb index 1b3566f9..0a5b279e 100644 --- a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb +++ b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb @@ -22,10 +22,6 @@ "import requests\n", "from tqdm import tqdm\n", "\n", - "from aymurai.experiments.entity_disambiguation.runner import (\n", - " call_extraction_api as extract_document,\n", - ")\n", - "\n", "API_URL = \"http://localhost:8000\" # Url for debugger. change it to your own\n", "DATA_ROOT = Path(\n", " os.getenv(\n", @@ -78,6 +74,135 @@ "## /document-extract endpoint output\n" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import mimetypes\n", + "import time\n", + "from pathlib import Path\n", + "from typing import Any, Iterable\n", + "\n", + "from more_itertools import unique_everseen\n", + "\n", + "\n", + "def discover_documents(root: Path, extensions: Iterable[str]) -> list[Path]:\n", + " \"\"\"\n", + " Discover documents in a directory with given extensions.\n", + "\n", + " Args:\n", + " root (Path): Root directory to search for documents.\n", + " extensions (Iterable[str]): File extensions to include.\n", + "\n", + " Returns:\n", + " list[Path]: List of discovered document paths with the specified extensions.\n", + " \"\"\"\n", + " extensions = {ext.lower() for ext in extensions}\n", + " return sorted(\n", + " path\n", + " for path in root.rglob(\"*\")\n", + " if path.is_file() and path.suffix.lower() in extensions\n", + " )\n", + "\n", + "\n", + "def call_extraction_api(\n", + " session: requests.Session, endpoint: str, file_path: Path, timeout_s: float\n", + ") -> dict[str, object]:\n", + " \"\"\"\n", + " Call the extraction API with a document file.\n", + "\n", + " Args:\n", + " session (requests.Session): HTTP session for making requests.\n", + " endpoint (str): URL of the extraction API endpoint.\n", + " file_path (Path): Path to the document file to be processed.\n", + " timeout_s (float): Request timeout in seconds.\n", + "\n", + " Returns:\n", + " dict[str, object]: Payload containing the response details.\n", + " \"\"\"\n", + " payload: dict[str, object] = {\n", + " \"path\": str(file_path),\n", + " \"status\": \"failure\",\n", + " \"status_code\": None,\n", + " \"elapsed_s\": None,\n", + " \"detail\": None,\n", + " }\n", + "\n", + " if not file_path.exists():\n", + " payload[\"detail\"] = \"File does not exist\"\n", + " return payload\n", + "\n", + " mime_type = mimetypes.guess_type(file_path.name)[0] or \"application/octet-stream\"\n", + " files = {\n", + " \"file\": (file_path.name, file_path.open(\"rb\"), mime_type),\n", + " }\n", + "\n", + " try:\n", + " start = time.perf_counter()\n", + " response = session.post(\n", + " endpoint,\n", + " files=files,\n", + " timeout=timeout_s,\n", + " )\n", + " elapsed = time.perf_counter() - start\n", + " except requests.RequestException as exc:\n", + " payload[\"detail\"] = f\"Request failed: {exc}\"\n", + " return payload\n", + " finally:\n", + " files[\"file\"][1].close()\n", + "\n", + " payload[\"status_code\"] = response.status_code\n", + " payload[\"elapsed_s\"] = elapsed\n", + "\n", + " try:\n", + " response_body = response.json()\n", + " except ValueError:\n", + " response_body = {\"raw\": response.text[:500]}\n", + "\n", + " if response.ok:\n", + " payload[\"status\"] = \"success\"\n", + " payload[\"detail\"] = {\n", + " \"document_id\": response_body.get(\"document_id\"),\n", + " \"document\": response_body.get(\"document\", []),\n", + " }\n", + " else:\n", + " payload[\"detail\"] = response_body\n", + "\n", + " return payload\n", + "\n", + "\n", + "def parse_prediction_labels(predictions: list[dict[str, Any]]) -> list[dict[str, str]]:\n", + " \"\"\"\n", + " Parse prediction labels to extract unique aymurai labels and their alternative texts.\n", + "\n", + " Args:\n", + " predictions (list[dict[str, Any]]): List of prediction dictionaries.\n", + "\n", + " Returns:\n", + " list[dict[str, str]]: List of dictionaries containing unique aymurai labels and their alternative texts.\n", + " \"\"\"\n", + " attrs_stream = (\n", + " label.get(\"attrs\") or {}\n", + " for label in (label for pred in predictions for label in pred.get(\"labels\", ()))\n", + " )\n", + "\n", + " unique_pairs = unique_everseen(\n", + " (\n", + " attrs.get(\"aymurai_label\"),\n", + " attrs.get(\"aymurai_alt_text\"),\n", + " )\n", + " for attrs in attrs_stream\n", + " if attrs.get(\"aymurai_label\") and attrs.get(\"aymurai_alt_text\")\n", + " )\n", + "\n", + " return sorted(\n", + " ({\"aymurai_label\": label, \"text\": text} for label, text in unique_pairs),\n", + " key=lambda item: (item[\"aymurai_label\"], item[\"text\"]),\n", + " )" + ] + }, { "cell_type": "code", "execution_count": null, @@ -86,7 +211,7 @@ "source": [ "# /document-extract endpoint output\n", "session = requests.Session()\n", - "document = extract_document(\n", + "document = call_extraction_api(\n", " session,\n", " endpoint=f\"{API_URL}/misc/document-extract\",\n", " file_path=doc_path,\n", From d02e20fe3bc3728009d6969220fe8dd51cbe25ad Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:37:39 +0000 Subject: [PATCH 067/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20TESSDATA=5FPRE?= =?UTF-8?q?FIX=20from=20.env.common?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.common | 2 -- 1 file changed, 2 deletions(-) diff --git a/.env.common b/.env.common index 1eec416e..493770c9 100644 --- a/.env.common +++ b/.env.common @@ -4,7 +4,5 @@ AYMURAI_CACHE_BASEPATH=/resources/cache/aymurai HF_HOME=/resources/cache/huggingface TOKENIZERS_PARALLELISM=1 -TESSDATA_PREFIX=/usr/local/share/tessdata - AYMURAI_RESTRICTED_DOCUMENT_PDFS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO-pdf" AYMURAI_RESTRICTED_DOCUMENT_DOCS_PATH="/resources/data/restricted/ar-juz-pcyf-10/RESOLUCIONES DEL JUZGADO" From eef81c606dddece830fb1f966dad8d26f91ddb3f Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:43:27 +0000 Subject: [PATCH 068/101] =?UTF-8?q?=F0=9F=99=88=20Update=20.gitignore=20to?= =?UTF-8?q?=20include=20notebooks=20directory=20while=20excluding=20subdir?= =?UTF-8?q?ectories=20and=20non-IPYNB=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 5f22f5f3..effd04a3 100755 --- a/.gitignore +++ b/.gitignore @@ -128,3 +128,7 @@ resources .venv aymurai/version.py + +notebooks/** +!notebooks/**/ +!notebooks/**/*.ipynb From 7f8ebfa57a936eca7126e8082caa8079485f9b38 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:43:43 +0000 Subject: [PATCH 069/101] =?UTF-8?q?=F0=9F=94=80=20Synthesize=20docker-comp?= =?UTF-8?q?ose=20from=2026033a8f/00709164=20after=20b05b768=20rollback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 83 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3ce7c536..0f7775ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,29 +1,72 @@ +x-api-lite: &api-lite + image: ghcr.io/aymurai/api:latest + build: + context: . + dockerfile: ./docker/api/Dockerfile + target: aymurai-api + shm_size: "2gb" + ports: + - "8899:8899" + volumes: + - ./resources/cache:/resources/cache + env_file: + - .env + - .env.common + +x-api-full: &api-full + image: ghcr.io/aymurai/api:full + build: + context: . + dockerfile: ./docker/api/Dockerfile + target: aymurai-api-full + shm_size: "2gb" + ports: + - "8899:8899" + restart: always + deploy: + resources: + limits: + memory: 4g + cpus: "4.0" + services: aymurai-api: - image: ghcr.io/aymurai/api:latest - ports: - - "8899:8899" - build: - context: . - dockerfile: ./docker/api/Dockerfile - target: aymurai-api - env_file: - - .env - - .env.common - volumes: - - ./resources/cache:/resources/cache + <<: *api-lite + container_name: aymurai-api + environment: + TORCH_DEVICE: cpu + + aymurai-api-gpu: + <<: *api-lite + container_name: aymurai-api-gpu + environment: + TORCH_DEVICE: cuda + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] aymurai-api-full: - image: ghcr.io/aymurai/api:full - ports: - - "8899:8899" - build: - context: . - dockerfile: ./docker/api/Dockerfile - target: aymurai-api-full - restart: always + <<: *api-full + container_name: aymurai-api-full + environment: + TORCH_DEVICE: cpu + + aymurai-api-full-gpu: + <<: *api-full + container_name: aymurai-api-full-gpu + environment: + TORCH_DEVICE: cuda deploy: resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] limits: memory: 4g cpus: "4.0" From b0233e59387b2c285e6e1c9057169e66390f1f7c Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:43:52 +0000 Subject: [PATCH 070/101] =?UTF-8?q?=F0=9F=94=80=20Synthesize=20Makefile=20?= =?UTF-8?q?from=20afbfda9/d80f74b/26033a8f=20after=20f645881=20rollback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 0f132348..816c1c5b 100644 --- a/Makefile +++ b/Makefile @@ -3,21 +3,37 @@ export $(shell sed 's/=.*//' .env) include .env.common export $(shell sed 's/=.*//' .env.common) +# Select which API service to control (override with API_SERVICE=aymurai-api-gpu) +API_SERVICE ?= aymurai-api +# Select which full API service to control (override with API_FULL_SERVICE=aymurai-api-full-gpu) +API_FULL_SERVICE ?= aymurai-api-full api-build: - docker compose build aymurai-api-dev + docker compose build $(API_SERVICE) api-run: - docker compose run --service-ports aymurai-api-dev + docker compose run --service-ports $(API_SERVICE) +api-up: + docker compose up -d $(API_SERVICE) +api-stop: + docker compose stop $(API_SERVICE) +api-logs: + docker compose logs -f $(API_SERVICE) api-pull: - docker compose pull aymurai-api + docker compose pull $(API_SERVICE) api-full-build: - docker compose build aymurai-api-full + docker compose build $(API_FULL_SERVICE) api-full-run: - docker compose run aymurai-api-full + docker compose run --service-ports $(API_FULL_SERVICE) +api-full-up: + docker compose up -d $(API_FULL_SERVICE) +api-full-stop: + docker compose stop $(API_FULL_SERVICE) +api-full-logs: + docker compose logs -f $(API_FULL_SERVICE) api-full-pull: - docker compose pull aymurai-api-full + docker compose pull $(API_FULL_SERVICE) stress-test: locust -f locustfile.py --host http://localhost:8899 @@ -27,4 +43,4 @@ alembic-regenerate: rm -rvf aymurai/database/versions/* && \ cd aymurai && \ uv run alembic revision --autogenerate -m "Create database" && \ - uv run alembic upgrade head \ No newline at end of file + uv run alembic upgrade head From ca04d8bdc5113bf0b434a4a4684e1db5078999d0 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:44:59 +0000 Subject: [PATCH 071/101] =?UTF-8?q?=F0=9F=94=A7=20Fix=20repository=20URL?= =?UTF-8?q?=20case=20sensitivity=20in=20pyproject.toml=20and=20remove=20un?= =?UTF-8?q?used=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8a94a2b9..1879831c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,6 @@ maintainers = [ description = "The backend API and machine learning models of AymurAI." readme = "README.md" license = { file = "LICENSE.md" } -# homepage = "https://github.com/AymurAI/backend" keywords = [ "api", "deep-learning", @@ -89,7 +88,7 @@ dependencies = [ [project.urls] Homepage = "https://www.aymurai.info/" -Repository = "https://github.com/aymurAI/backend" +Repository = "https://github.com/AymurAI/backend" Issues = "https://github.com/AymurAI/backend/issues" [dependency-groups] @@ -102,9 +101,6 @@ dev = [ "nbstripout>=0.8.0", "jupyter>=1.1.1", "pip>=24.3.1", - "streamlit>=1.21.0", - "dspy>=3.0.4", - "playwright==1.56.0" ] [tool.setuptools.packages.find] From 203f33ef549324cdd77e6d86c6902cc453c8d239 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:45:12 +0000 Subject: [PATCH 072/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20tasks.json=20co?= =?UTF-8?q?nfiguration=20for=20Ollama=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/tasks.json | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 6283a7ed..00000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "Start Ollama service", - "type": "shell", - "command": "make", - "args": [ - "ollama-up" - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - } - ] -} \ No newline at end of file From 24825e105914a0b61f841f9f407a66b8f2c138f8 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:45:57 +0000 Subject: [PATCH 073/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20scraper=20and?= =?UTF-8?q?=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scraper/README.md | 97 ----- scraper/playwright_scraper.py | 739 ---------------------------------- 2 files changed, 836 deletions(-) delete mode 100644 scraper/README.md delete mode 100644 scraper/playwright_scraper.py diff --git a/scraper/README.md b/scraper/README.md deleted file mode 100644 index dc965075..00000000 --- a/scraper/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Scraper Playwright - Gestión Documental PJN - -Este repositorio contiene un script en Python que usa **Playwright** para automatizar la recolección -de documentos PDF publicados en la sección de gestión documental del Poder Judicial de la Nación -(https://www.pjn.gov.ar/gestion-documental). El scraper reproduce las llamadas AJAX que genera el front-end, -mantiene las cookies que requiere el API y descarga los adjuntos desde el servicio oficial. - -## Requisitos - -1. Python 3.8+ -2. Playwright y el navegador Chromium. - -```bash -python -m pip install --upgrade pip -pip install playwright -playwright install chromium -``` - -## Uso básico - -```bash -python playwright_scraper.py --output-dir datos --max-pages 5 --per-page 50 --pdf-template "https://pjn-documento-api.pjn.gov.ar/api/documento/adjunto/{id}" -``` - -El script hará: - -- Abrirá una sesión de Playwright y visitará la página oficial. -- Detectará automáticamente qué llamada REST obtiene los documentos. -- Iterará páginas y descargará los PDFs dentro del directorio `datos`. - -## Aplicar filtros desde la línea de comandos - -La interfaz web permite limitar por dependencia, categorías y rango de fechas. El scraper replica ese comportamiento con los argumentos: - -- `--dependencia "Fueros Federales"` aplica ese texto tanto a `dependencia` como a `dependenciaTexto`. -- `--category` se puede repetir para simular clics en categorías/subcategorías anidadas (por ejemplo `--category Reglamentos --category Normativa`). -- `--desde` y `--hasta` definen los campos `fechaDesde`/`fechaHasta` (o `desde`/`hasta`) que el backend espera. -- `--filter clave=valor` te deja añadir cualquier parámetro extra (por ejemplo `--filter orden=desc`, `--filter tipoDocumento=Resolución`). -- `--simulate-ui` simula los clicks sobre filtros, expande los botones “Mostrar más” y rellena las fechas como lo hace la UI antes de capturar la llamada AJAX. Úsalo si necesitás replicar exactamente la interacción de navegación para los filtros que ves en pantalla. -- `--debug-ui` habilita registros detallados sobre cada intento de clic/expansión para que puedas ver en consola qué filtros se están tocando en tiempo real. - -Los filtros se mezclan automáticamente en la query y en el cuerpo de la petición, así que puedes combinar varios `-f` en el mismo comando. - -## Estructura de almacenamiento guiada por filtros - -Los documentos descargados se organizan siguiendo los filtros aplicados y la metadata del registro: - -- Cada PDF se guarda en `ruta-de-salida///`, usando la dependencia que especifiques o la que entrega el API. -- La categoría se extrae de campos como `rubro`, `categoria` o `tipo` cuando están presentes. -- Si pasás `--category` el scraper usa esa secuencia de categorías/subcategorías como niveles adicionales (en lugar de los valores devueltos por el API), lo que refleja la navegación que aplicaste en los filtros. -- Si falta dependencia o año, se usan nombres como `sin-dependencia` o `sin-anio` para evitar colisiones. -- Los nombres de archivo ahora conservan acentos y caracteres especiales porque la normalización usa Unicode completo (`NFC`) antes de reemplazar espacios; así el título original permanece íntegro para tu pipeline de metadata. - -Esto facilita tener directorios alineados con las dependencias del Poder Judicial y continuar el pipeline de anonimización con una estructura consistente. - -Puedes ajustar el número de páginas, el retraso entre peticiones o el límite de documentos con -`--delay`, `--max-documents`, `--max-pages`. - -## Firma manual del API - -Si lo prefieres, puedes evitar la detección automática y proporcionar un JSON con la firma de las peticiones. -Esto es útil cuando ya sabes qué endpoint se usa o necesitas definir filtros específicos. - -```json -{ - "method": "POST", - "base_url": "https://www.pjn.gov.ar/api/documento/paginado", - "headers": { - "Accept": "application/json", - "Referer": "https://www.pjn.gov.ar/gestion-documental" - }, - "query_params": { - "orden": "desc" - }, - "json_body": { - "pagina": 0, - "size": 25, - "dependencia": "Fueros Federales" - }, - "listing_path": ["data", "registros"], - "page_keys": { - "pagina": 0 - }, - "size_keys": { - "size": 25 - }, - "body_type": "json" -} -``` - -Guarda ese JSON y pásalo al script con `--signature-file firma.json`. - -## Siguientes pasos - -1. Define reglas de nombrado y almacenamiento según tu pipeline de anonimización. -2. Agrega paralelismo/control de errores si necesitas escalar la descarga. -3. Encadena el dump resultante con los módulos de extracción y validación de Aymurai. diff --git a/scraper/playwright_scraper.py b/scraper/playwright_scraper.py deleted file mode 100644 index f108563e..00000000 --- a/scraper/playwright_scraper.py +++ /dev/null @@ -1,739 +0,0 @@ -#!/usr/bin/env python3 -""" -Playwright scraper para la sección “Gestión Documental” del Poder Judicial de la Nación. - -Este script usa Playwright para capturar las cabeceras y cookies que el frontend genera -y después reproduce las llamadas al API para descargar en lote los PDFs que se enumeran -en la interfaz. Se detecta de forma dinámica el endpoint y los parámetros de paginación, -pero también puede recibir una firma manual si así se prefiere. -""" - -import argparse -import asyncio -import copy -import json -import logging -import re -import unicodedata -import urllib.parse -from dataclasses import dataclass -from datetime import datetime -from pathlib import Path -from typing import Any, Dict, Iterable, List, Optional, Tuple - -from playwright.async_api import APIRequestContext, Page, async_playwright - -logging.basicConfig( - level=logging.INFO, - format="[%(asctime)s] %(levelname)s %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", -) -logger = logging.getLogger(__name__) - - -PAGE_KEYWORDS = ( - "pagina", - "page", - "pageindex", - "pagenumber", - "paginaactual", - "numeropagina", - "offset", -) -SIZE_KEYWORDS = ( - "size", - "pagesize", - "tamanio", - "cantidad", - "limit", - "perpage", - "per_page", -) - -MORE_BUTTON_SELECTOR = "text=/Mostrar\\s+m[aá]s/i" - - -def safe_filename(value: str) -> str: - clean = unicodedata.normalize("NFC", value.strip()) - clean = re.sub(r"\s+", "-", clean) - clean = re.sub(r"[^\w\-\.]", "", clean) - return clean[:120] - - -def detect_numeric_fields( - container: Dict[str, Any], keywords: Iterable[str] -) -> Dict[str, int]: - matches: Dict[str, int] = {} - for key, value in container.items(): - lowered = key.lower() - if any(keyword in lowered for keyword in keywords): - try: - matches[key] = int(value) - except (TypeError, ValueError): - continue - return matches - - -def update_values( - container: Dict[str, Any], keys: Iterable[str], new_value: int -) -> None: - for key in keys: - if key in container: - container[key] = new_value - - -def strip_sensitive_headers(headers: Dict[str, str]) -> Dict[str, str]: - blocked = { - "content-length", - "origin", - "connection", - "accept-encoding", - } - return {k: v for k, v in headers.items() if k.lower() not in blocked} - - -def load_signature_from_file(path: Path) -> Dict[str, Any]: - with path.open("r", encoding="utf-8") as fh: - return json.load(fh) - - -def extract_attachment_id(record: Dict[str, Any]) -> Optional[str]: - candidates = ( - "idAdjunto", - "adjunto", - "idDocumento", - "documentoAdjunto", - "id", - "adjuntoId", - ) - for key in candidates: - value = record.get(key) - if isinstance(value, dict): - nested = value.get("id") or value.get("codigo") - if nested: - return str(nested) - elif value: - return str(value) - return None - - -def record_field(record: Dict[str, Any], keys: Iterable[str]) -> Optional[str]: - for key in keys: - if key in record and record[key]: - return str(record[key]) - return None - - -def find_listing_path( - payload: Any, path: Tuple[str, ...] = () -) -> Optional[Tuple[Tuple[str, ...], List[Dict[str, Any]]]]: - if isinstance(payload, dict): - for key, value in payload.items(): - if ( - isinstance(value, list) - and value - and all(isinstance(item, dict) for item in value) - ): - if any( - any( - "id" in field.lower() or "adjunto" in field.lower() - for field in item.keys() - ) - for item in value - ): - return path + (key,), value - child = find_listing_path(value, path + (key,)) - if child: - return child - elif isinstance(payload, list): - if payload and all(isinstance(item, dict) for item in payload): - if any( - any( - "id" in field.lower() or "adjunto" in field.lower() - for field in item.keys() - ) - for item in payload - ): - return path, payload - return None - - -def extract_records_by_path( - payload: Dict[str, Any], path: Tuple[str, ...] -) -> List[Dict[str, Any]]: - current: Any = payload - for key in path: - if not isinstance(current, dict): - return [] - current = current.get(key) - if current is None: - return [] - if isinstance(current, list): - return [item for item in current if isinstance(item, dict)] - return [] - - -def derive_pdf_url(template: str, attachment_id: str) -> str: - if "{id}" in template: - return template.format(id=attachment_id) - return template.rstrip("/") + "/" + attachment_id - - -@dataclass -class SearchSignature: - method: str - base_url: str - headers: Dict[str, str] - query_params: Dict[str, str] - json_body: Optional[Dict[str, Any]] - listing_path: Tuple[str, ...] - example_record: Dict[str, Any] - page_keys: Dict[str, int] - size_keys: Dict[str, int] - body_type: str - - -def build_signature_from_request( - request, - listing_path: Tuple[str, ...], - example_record: Dict[str, Any], - response_payload: Dict[str, Any], -) -> SearchSignature: - parsed = urllib.parse.urlparse(request.url) - base_url = urllib.parse.urlunparse(parsed._replace(query="")) - query_params = dict(urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)) - method = request.method - headers = strip_sensitive_headers(dict(request.headers)) - json_body: Optional[Dict[str, Any]] = None - post_data = request.post_data - content_type = request.headers.get("content-type", "") - if post_data: - if "x-www-form-urlencoded" in content_type: - json_body = dict(urllib.parse.parse_qsl(post_data)) - else: - try: - json_body = json.loads(post_data) - except json.JSONDecodeError: - logger.debug("No se pudo parsear cuerpo JSON de la petición inicial.") - initial_container = {} - initial_container.update(query_params) - if isinstance(json_body, dict): - initial_container.update(json_body) - page_keys = detect_numeric_fields(initial_container, PAGE_KEYWORDS) - size_keys = detect_numeric_fields(initial_container, SIZE_KEYWORDS) - if not listing_path: - found = find_listing_path(response_payload) - if not found: - raise RuntimeError( - "No se pudo determinar la ruta de listado de documentos." - ) - listing_path = found[0] - if post_data: - if "x-www-form-urlencoded" in content_type: - body_type = "form" - else: - body_type = "json" - else: - body_type = "none" - return SearchSignature( - method=method.upper(), - base_url=base_url, - headers=headers, - query_params=query_params, - json_body=json_body, - listing_path=listing_path, - example_record=example_record, - page_keys=page_keys, - size_keys=size_keys, - body_type=body_type, - ) - - -async def capture_signature( - page: Page, args: argparse.Namespace, timeout: float = 20.0 -) -> SearchSignature: - future = asyncio.get_running_loop().create_future() - - async def inspect_response(response): - if future.done(): - return - if response.status != 200: - return - if response.request.resource_type != "xhr": - return - content_type = response.headers.get("content-type", "") - if "application/json" not in content_type: - return - try: - payload = await response.json() - except Exception: - return - found = find_listing_path(payload) - if not found: - return - records = found[1] - if not records: - return - future.set_result((response.request, found[0], records[0], payload)) - - page.on( - "response", - lambda response: asyncio.create_task(inspect_response(response)), - ) - await page.goto( - "https://www.pjn.gov.ar/gestion-documental", wait_until="networkidle" - ) - if args.simulate_ui: - try: - await apply_ui_filters(page, args) - except Exception as exc: # pragma: no cover - best effort - logger.warning("Error al aplicar filtros UI: %s", exc) - try: - await asyncio.wait_for(future, timeout=timeout) - except asyncio.TimeoutError: - logger.warning( - "No se detectó automáticamente la llamada al API. Asegúrate de navegar en la página." - ) - raise - - request, listing_path, sample_record, payload = future.result() - signature = build_signature_from_request( - request, listing_path, sample_record, payload - ) - logger.info("Firma detectada: %s %s", signature.method, signature.base_url) - logger.debug("Campos página: %s", signature.page_keys) - logger.debug("Campos tamaño: %s", signature.size_keys) - return signature - - -def load_signature(source: Dict[str, Any]) -> SearchSignature: - signature = SearchSignature( - method=source["method"].upper(), - base_url=source["base_url"], - headers=source.get("headers", {}), - query_params=source.get("query_params", {}), - json_body=source.get("json_body"), - listing_path=tuple(source["listing_path"]), - example_record=source.get("example_record", {}), - page_keys={k: int(v) for k, v in source.get("page_keys", {}).items()}, - size_keys={k: int(v) for k, v in source.get("size_keys", {}).items()}, - body_type=source.get("body_type", "json"), - ) - return signature - - -def prepare_page_payload( - signature: SearchSignature, - page_value: int, - page_size: int, -) -> Tuple[Dict[str, str], Optional[Dict[str, Any]]]: - query_params = signature.query_params.copy() - if signature.json_body is not None: - body_payload = copy.deepcopy(signature.json_body) - else: - body_payload = None - if signature.page_keys: - update_values(query_params, signature.page_keys.keys(), page_value) - if body_payload is not None: - update_values(body_payload, signature.page_keys.keys(), page_value) - if signature.size_keys: - update_values(query_params, signature.size_keys.keys(), page_size) - if body_payload is not None: - update_values(body_payload, signature.size_keys.keys(), page_size) - return query_params, body_payload - - -def apply_filter_overrides( - query_params: Dict[str, str], - body_payload: Optional[Dict[str, Any]], - overrides: Dict[str, str], -) -> None: - if not overrides: - return - for key, value in overrides.items(): - query_params[key] = value - if body_payload is not None: - body_payload[key] = value - - -def build_filter_overrides( - raw_filters: Iterable[str], - dependencia: Optional[str], - desde: Optional[str], - hasta: Optional[str], -) -> Dict[str, str]: - overrides: Dict[str, str] = {} - for option in raw_filters: - if "=" not in option: - raise ValueError(f"Filtro inválido: {option!r}. Use clave=valor.") - key, value = option.split("=", 1) - overrides[key] = value - if dependencia: - overrides.setdefault("dependencia", dependencia) - overrides.setdefault("dependenciaTexto", dependencia) - if desde: - overrides.setdefault("fechaDesde", desde) - overrides.setdefault("desde", desde) - if hasta: - overrides.setdefault("fechaHasta", hasta) - overrides.setdefault("hasta", hasta) - return overrides - - -def ui_debug(args: argparse.Namespace, message: str) -> None: - if getattr(args, "debug_ui", False): - logger.info("[UI] %s", message) - - -async def expand_more_filters(page: Page, args: argparse.Namespace) -> None: - while True: - locator = page.locator(MORE_BUTTON_SELECTOR) - if not await locator.count(): - break - ui_debug(args, "Expandiendo filtros con 'Mostrar más'") - await locator.first.click() - await page.wait_for_timeout(250) - - -async def click_filter_option(page: Page, args: argparse.Namespace, text: str) -> bool: - text = text.strip() - if not text: - return False - strategies = [ - ("role=button", page.get_by_role("button", name=text, exact=False)), - ("role=link", page.get_by_role("link", name=text, exact=False)), - ("text-match", page.get_by_text(text, exact=False)), - ("button:has-text", page.locator(f'button:has-text("{text}")')), - ("div:has-text", page.locator(f'div:has-text("{text}")')), - ] - ui_debug( - args, - f"Intentando seleccionar filtro '{text}' usando {len(strategies)} estrategias", - ) - for description, locator in strategies: - ui_debug(args, f"Probando estrategia '{description}' para '{text}'") - if await locator.count(): - ui_debug(args, f"Clic en '{text}' usando '{description}'") - await locator.first.click() - await page.wait_for_timeout(400) - return True - ui_debug(args, f"No se encontró '{text}' con '{description}'") - return False - - -def normalize_ui_date(value: str) -> str: - try: - parsed = datetime.fromisoformat(value) - return parsed.strftime("%d/%m/%Y") - except ValueError: - return value - - -async def apply_ui_filters(page: Page, args: argparse.Namespace) -> None: - await expand_more_filters(page, args) - if args.dependencia: - clicked = await click_filter_option(page, args, args.dependencia) - if not clicked: - logger.warning( - "No se encontró la dependencia %s en el filtro UI.", args.dependencia - ) - await expand_more_filters(page, args) - for category in getattr(args, "category_path", []): - await expand_more_filters(page, args) - clicked = await click_filter_option(page, args, category) - if not clicked: - logger.warning("No se encontró la categoría %s en el filtro UI.", category) - await expand_more_filters(page, args) - if args.desde: - desde_input = page.locator("input[placeholder='Desde']") - if await desde_input.count(): - await desde_input.fill(normalize_ui_date(args.desde)) - if args.hasta: - hasta_input = page.locator("input[placeholder='Hasta']") - if await hasta_input.count(): - await hasta_input.fill(normalize_ui_date(args.hasta)) - apply_button = page.locator("button:has-text('Aplicar')") - if not await apply_button.count(): - apply_button = page.locator("button:has-text('Buscar')") - if await apply_button.count(): - ui_debug(args, "Aplicando filtros (botón 'Aplicar'/'Buscar')") - await apply_button.first.click() - else: - await page.keyboard.press("Enter") - await page.wait_for_timeout(1500) - - -def extract_year(value: Optional[str]) -> str: - if not value: - return "sin-anio" - match = re.search(r"(19|20)\d{2}", value) - return match.group(0) if match else "sin-anio" - - -def determine_document_path( - args: argparse.Namespace, record: Dict[str, Any], document: Dict[str, str] -) -> Path: - dependency = ( - args.dependencia - or document.get("dependency") - or record_field(record, ("dependencia", "origen", "tribunal")) - or "sin-dependencia" - ) - category = record_field( - record, ("rubro", "categoria", "tipo", "clasificacion", "subcategoria") - ) - year = extract_year(document.get("date")) - segments = [args.output_dir, safe_filename(dependency)] - if getattr(args, "category_path", None): - segments.extend( - safe_filename(part) for part in args.category_path if part and part.strip() - ) - elif category: - segments.append(safe_filename(category)) - segments.append(year) - return Path(*segments) / document["file_name"] - - -def build_document( - record: Dict[str, Any], pdf_template: str -) -> Optional[Dict[str, str]]: - attachment_id = extract_attachment_id(record) - if not attachment_id: - return None - pdf_url = derive_pdf_url(pdf_template, attachment_id) - document_date = record_field(record, ("fecha", "fechaDocumento", "fechaResolucion")) - title = record_field(record, ("titulo", "descripcion", "tituloDocumento", "nombre")) - dependency = record_field( - record, ("dependencia", "dependenciaTexto", "origen", "tribunal") - ) - parts = [ - document_date or "fecha-desconocida", - title or "sin-titulo", - dependency or "dependencia", - attachment_id, - ] - name = safe_filename("_".join(part for part in parts if part)) - return { - "attachment_id": attachment_id, - "pdf_url": pdf_url, - "file_name": f"{name}.pdf", - "title": title or "", - "date": document_date or "", - "dependency": dependency or "", - } - - -async def download_document( - context: APIRequestContext, destination: Path, document: Dict[str, str] -) -> None: - destination.parent.mkdir(parents=True, exist_ok=True) - if destination.exists(): - logger.info("Salteando %s, ya existe", destination.name) - return - response = await context.get( - document["pdf_url"], headers={"Referer": "https://www.pjn.gov.ar/"} - ) - response_ok = response.ok - if not response_ok: - logger.warning( - "Error al obtener %s (%s)", - document["attachment_id"], - response.status, - ) - return - content = await response.body() - destination.write_bytes(content) - logger.info("Descargado %s (%s)", destination.name, len(content)) - - -async def run_scraping( - args: argparse.Namespace, - signature: SearchSignature, - context_request: APIRequestContext, -) -> None: - documents_downloaded = 0 - page_start = ( - args.page_start - if args.page_start is not None - else next(iter(signature.page_keys.values()), 0) - ) - page_size = ( - args.page_size - if args.page_size is not None - else next(iter(signature.size_keys.values()), args.per_page) - ) - for page_index in range(args.max_pages): - if args.max_documents and documents_downloaded >= args.max_documents: - logger.info("Se alcanzó el límite de documentos solicitado.") - break - page_value = page_start + page_index - query_params, body_payload = prepare_page_payload( - signature, page_value, page_size - ) - apply_filter_overrides(query_params, body_payload, args.filter_overrides) - if signature.method == "GET": - response = await context_request.get( - signature.base_url, - params=query_params, - headers=signature.headers, - ) - else: - if body_payload is not None: - if signature.body_type == "form": - response = await context_request.post( - signature.base_url, - params=query_params, - data=body_payload, - headers=signature.headers, - ) - else: - response = await context_request.post( - signature.base_url, - params=query_params, - json=body_payload, - headers=signature.headers, - ) - else: - response = await context_request.post( - signature.base_url, - params=query_params, - headers=signature.headers, - ) - if not response.ok: - logger.warning("Respuesta %s en página %s", response.status, page_value) - break - payload = await response.json() - records = extract_records_by_path(payload, signature.listing_path) - if not records: - logger.info("No hay registros en la página %s", page_value) - break - logger.info("Procesando página %s (%d registros)", page_value, len(records)) - for record in records: - if args.max_documents and documents_downloaded >= args.max_documents: - break - doc = build_document(record, args.pdf_template) - if not doc: - continue - destination = determine_document_path(args, record, doc) - await download_document(context_request, destination, doc) - documents_downloaded += 1 - await asyncio.sleep(args.delay) - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Scraper de gestión documental usando Playwright" - ) - parser.add_argument( - "--output-dir", - type=Path, - default=Path("descargas"), - help="Carpeta donde se guardan los PDFs", - ) - parser.add_argument( - "--pdf-template", - default="https://pjn-documento-api.pjn.gov.ar/api/documento/adjunto/{id}", - ) - parser.add_argument("--max-pages", type=int, default=20) - parser.add_argument("--per-page", type=int, default=25) - parser.add_argument( - "-f", - "--filter", - action="append", - default=[], - help="Filtros adicionales como clave=valor (aplica a query y/o cuerpo).", - ) - parser.add_argument( - "--dependencia", - help="Filtra por dependencia (ej. 'Fueros Federales' o 'Consejo de la Magistratura').", - ) - parser.add_argument( - "--desde", - help="Fecha mínima de publicación en formato ISO o compatible con el backend.", - ) - parser.add_argument( - "--hasta", - help="Fecha máxima de publicación en formato ISO o compatible con el backend.", - ) - parser.add_argument( - "--category", - action="append", - default=[], - help="Secuencia de categorías/subcategorías a seleccionar tras la dependencia.", - ) - parser.add_argument( - "--simulate-ui", - action="store_true", - help="Simula clicks/fechas en la UI antes de detectar la llamada REST.", - ) - parser.add_argument( - "--debug-ui", - action="store_true", - help="Imprime en consola cada intento de interacción con los filtros.", - ) - parser.add_argument( - "--delay", - type=float, - default=1.5, - help="Retraso entre páginas para respetar al servidor", - ) - parser.add_argument( - "--max-documents", - type=int, - default=0, - help="Máximo de PDFs a guardar (0 = infinito)", - ) - parser.add_argument( - "--headless", - action="store_true", - help="Ejecutar Playwright en modo headless", - ) - parser.add_argument( - "--signature-file", - type=Path, - help="Ruta a un JSON con la firma del API", - ) - parser.add_argument( - "--page-start", - type=int, - help="Valor base para el primer número de página", - ) - parser.add_argument( - "--page-size", type=int, help="Cantidad de registros por página" - ) - return parser.parse_args() - - -def main() -> None: - args = parse_args() - if args.max_documents < 0: - raise ValueError("max-documents debe ser positivo o cero.") - args.output_dir.mkdir(parents=True, exist_ok=True) - args.category_path = args.category or [] - args.filter_overrides = build_filter_overrides( - args.filter, args.dependencia, args.desde, args.hasta - ) - - async def run(): - async with async_playwright() as p: - browser = await p.chromium.launch(headless=args.headless) - context = await browser.new_context(viewport={"width": 1400, "height": 900}) - page = await context.new_page() - signature: SearchSignature - if args.signature_file: - signature_data = load_signature_from_file(args.signature_file) - signature = load_signature(signature_data) - await page.goto( - "https://www.pjn.gov.ar/gestion-documental", - wait_until="networkidle", - ) - else: - signature = await capture_signature(page, args) - await run_scraping(args, signature, context.request) - await browser.close() - - asyncio.run(run()) - - -if __name__ == "__main__": - main() From a6986fee3d19546171bd4bdb4e6e6d02571d73e2 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:47:00 +0000 Subject: [PATCH 074/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20experiment=20mo?= =?UTF-8?q?dule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/experiments/__init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 aymurai/experiments/__init__.py diff --git a/aymurai/experiments/__init__.py b/aymurai/experiments/__init__.py deleted file mode 100644 index 78e64a4b..00000000 --- a/aymurai/experiments/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Experiment modules and helpers.""" From 968344d97f9200a5408646d80ed5c9c5964ad898 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:48:36 +0000 Subject: [PATCH 075/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20path=20utility?= =?UTF-8?q?=20functions=20from=20paths.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/evaluation/metrics.py | 1 - aymurai/utils/paths.py | 40 ----------------------------------- 2 files changed, 41 deletions(-) delete mode 100644 aymurai/utils/paths.py diff --git a/aymurai/evaluation/metrics.py b/aymurai/evaluation/metrics.py index bea0a785..ced35d99 100644 --- a/aymurai/evaluation/metrics.py +++ b/aymurai/evaluation/metrics.py @@ -7,7 +7,6 @@ from aymurai.logger import get_logger from aymurai.utils.json_data import load_json -from aymurai.utils.paths import prediction_filename_for_test, test_id_from_filename logger = get_logger(__name__) diff --git a/aymurai/utils/paths.py b/aymurai/utils/paths.py deleted file mode 100644 index 0677c72e..00000000 --- a/aymurai/utils/paths.py +++ /dev/null @@ -1,40 +0,0 @@ -from pathlib import Path - -TEST_SUFFIX = "-test" - - -def test_id_from_filename(test_path: Path, *, test_suffix: str = TEST_SUFFIX) -> str: - """ - Convert a test filename like "document-1-test.json" into "document-1". - Falls back to stripping ".json" when the suffix is not present. - - Args: - test_path (Path): Path to the test file. - test_suffix (str, optional): Suffix indicating test files. Defaults to "-test". - - Returns: - str: The test identifier extracted from the filename. - """ - name = Path(test_path).name - suffix_with_ext = f"{test_suffix}.json" - if name.endswith(suffix_with_ext): - return name[: -len(suffix_with_ext)] - if name.endswith(".json"): - return name[: -len(".json")] - return name - - -def prediction_filename_for_test( - test_path: Path, *, test_suffix: str = TEST_SUFFIX -) -> str: - """ - Return the prediction filename that should correspond to the test file. - - Args: - test_path (Path): Path to the test file. - test_suffix (str, optional): Suffix indicating test files. Defaults to "-test". - - Returns: - str: The prediction filename corresponding to the test file. - """ - return f"{test_id_from_filename(test_path, test_suffix=test_suffix)}.json" From 6c3514377083bd99e51c12b9f500651260522f0a Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:50:10 +0000 Subject: [PATCH 076/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20Prompt?= =?UTF-8?q?Set=20and=20PromptLibrary=20classes,=20and=20simplify=20disambi?= =?UTF-8?q?guation=20options=20in=20LabelPolicy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/meta/api_interfaces.py | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/aymurai/meta/api_interfaces.py b/aymurai/meta/api_interfaces.py index 1caa6509..ab1e8845 100644 --- a/aymurai/meta/api_interfaces.py +++ b/aymurai/meta/api_interfaces.py @@ -1,12 +1,10 @@ import uuid +from typing import Literal from pydantic import UUID5, BaseModel, Field, RootModel -from typing import Literal from aymurai.meta.entities import EntityAttributes -from functools import cached_property - class SuccessResponse(BaseModel): id: int | uuid.UUID | None = None @@ -49,7 +47,7 @@ class LabelPolicy(BaseModel): """Per-label policy for disambiguation and anonymization.""" anonymize: bool | None = None - disambiguation: Literal["none", "fuzzy", "llm"] | None = None + disambiguation: Literal["none", "fuzzy"] | None = None use_subclass_when_available: bool | None = None @@ -79,20 +77,3 @@ class Document(BaseModel): document_id: UUID5 header: list[str] | None = None footer: list[str] | None = None - - -class PromptSet(BaseModel): - label: str - system: str - user: str - - -class PromptLibrary(RootModel): - root: list[PromptSet] = Field(default_factory=list) - - @cached_property - def as_dict(self) -> dict[str, PromptSet]: - return {p.label: p for p in self.root} - - def get(self, label: str) -> PromptSet | None: - return self.as_dict.get(label) From 4d28e03dd261a0d7c595e5ee62fa32bdc128b906 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:50:28 +0000 Subject: [PATCH 077/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20EntityRelation?= =?UTF-8?q?=20class=20and=20its=20associated=20methods=20from=20entities.p?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/meta/entities.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/aymurai/meta/entities.py b/aymurai/meta/entities.py index 871ee462..759167c2 100644 --- a/aymurai/meta/entities.py +++ b/aymurai/meta/entities.py @@ -63,45 +63,6 @@ class Entity(BaseModel): attrs: EntityAttributes | None = None -class EntityRelation(BaseModel): - """Semantic relation between canonical entities.""" - - relation_id: UUID | None = Field( - None, description="Unique identifier of the relation" - ) - relation_type: str = Field( - description="Type of relation (e.g. identifies, resides_in)" - ) - subject_entity_id: UUID = Field( - description="Identifier of the subject entity participating in the relation" - ) - object_entity_id: UUID = Field( - description="Identifier of the object entity participating in the relation" - ) - attributes: dict[str, Any] = Field( - default_factory=dict, - description="Additional attributes captured for the relation", - ) - - @model_validator(mode="after") - def assign_relation_id(self) -> EntityRelation: - """Populate relation_id deterministically when not provided.""" - - if self.relation_id is not None: - return self - - payload = { - "relation_type": self.relation_type, - "subject_entity_id": str(self.subject_entity_id), - "object_entity_id": str(self.object_entity_id), - "attributes": self.attributes, - } - seed = json.dumps(payload, sort_keys=True, separators=(",", ":")) - object.__setattr__(self, "relation_id", uuid5(NAMESPACE_URL, seed)) - - return self - - class CanonicalEntity(BaseModel): """Canonical representation of an entity cluster.""" From e6f32baf1ebf241db34e5b27f60c723fa4c2052a Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 17:51:19 +0000 Subject: [PATCH 078/101] =?UTF-8?q?=F0=9F=93=9D=20Enhance=20documentation?= =?UTF-8?q?=20with=20detailed=20docstrings=20for=20various=20functions=20a?= =?UTF-8?q?cross=20multiple=20modules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/utils/alignment/core.py | 48 ++++++++++++++---- aymurai/utils/alignment/ia2.py | 53 +++++++++++++++++++- aymurai/utils/cache.py | 8 ++- aymurai/utils/display/pandas.py | 13 ++++- aymurai/utils/download.py | 7 ++- aymurai/utils/entity_disambiguation/core.py | 8 +-- aymurai/utils/entity_disambiguation/fuzzy.py | 7 +-- aymurai/utils/json_data.py | 9 ++-- aymurai/utils/json_encoding.py | 40 +++++++++++++-- aymurai/utils/misc.py | 50 ++++++++++++------ aymurai/utils/pickle_data.py | 18 ++++++- 11 files changed, 208 insertions(+), 53 deletions(-) diff --git a/aymurai/utils/alignment/core.py b/aymurai/utils/alignment/core.py index c8eeeed2..65a3004c 100644 --- a/aymurai/utils/alignment/core.py +++ b/aymurai/utils/alignment/core.py @@ -11,6 +11,15 @@ def tokenize(text: str) -> list[str]: + """ + Split multi-line text into whitespace-delimited tokens. + + Args: + text (str): Input text to tokenize. + + Returns: + list[str]: Tokens extracted from the text. + """ tokens = map(str.split, text.splitlines()) tokens = flatten(tokens) return list(tokens) @@ -21,15 +30,17 @@ def align_text( target_text: str, columns: tuple[str, str] = ("source", "target"), ) -> pd.DataFrame: - """align source and target text into a table + """ + Align source and target text into a token-level mapping table. Args: - source_text (str): reference text - target_text (str): second text to align with - columns (tuple[str, str]): names of columns on output + source_text (str): Reference text. + target_text (str): Text to align against `source_text`. + columns (tuple[str, str], optional): Output column names. + Defaults to ("source", "target"). Returns: - pd.DataFrame: alignment table + pd.DataFrame: Alignment table. """ source_tokens = [t.strip() for t in tokenize(source_text)] target_tokens = [t.strip() for t in tokenize(target_text)] @@ -78,15 +89,22 @@ def align_docs( columns: tuple[str, str] = ("source", "target"), source_preprocess: Callable[[str], str] | None = None, target_preprocess: Callable[[str], str] | None = None, -): - """align two documents word to word +) -> pd.DataFrame: + """ + Align two documents word by word. Args: - source_path (str | Path): source document path (reference) - target_path (str | Path): target document path (target) + source_path (str | Path): Source document path (reference). + target_path (str | Path): Target document path. + columns (tuple[str, str], optional): Output column names. + Defaults to ("source", "target"). + source_preprocess (Callable[[str], str] | None, optional): Preprocessing + function applied to extracted source text. Defaults to None. + target_preprocess (Callable[[str], str] | None, optional): Preprocessing + function applied to extracted target text. Defaults to None. Returns: - pd.DataFrame: alignment table + pd.DataFrame: Alignment table. """ source: str = extract_document(source_path, errors="raise") # type: ignore target: str = extract_document(target_path, errors="raise") # type: ignore @@ -140,6 +158,16 @@ def add_empty_lines_between_paragraphs( reference: str, mapping: pd.DataFrame, ) -> pd.DataFrame: + """ + Insert empty rows in an alignment table at paragraph break boundaries. + + Args: + reference (str): Reference text used to infer paragraph boundaries. + mapping (pd.DataFrame): Alignment table to augment. + + Returns: + pd.DataFrame: Alignment table with inserted empty rows. + """ mapping = mapping.copy() reference = re.sub(r"\n+", "\n", reference) splitted = reference.splitlines() diff --git a/aymurai/utils/alignment/ia2.py b/aymurai/utils/alignment/ia2.py index f94af1a3..68f7755a 100644 --- a/aymurai/utils/alignment/ia2.py +++ b/aymurai/utils/alignment/ia2.py @@ -1,5 +1,6 @@ import os import re +from typing import Any import numpy as np import pandas as pd @@ -7,12 +8,31 @@ def normalize(text: str) -> str: + """ + Normalize text by removing accents and non-word characters. + + Args: + text (str): Input text. + + Returns: + str: Normalized text. + """ text = unidecode(text) text = re.sub(r"\W", "", text) return text -def norm_ia2_label(text, labels: list[str]) -> str | float: +def norm_ia2_label(text: Any, labels: list[str]) -> str | float: + """ + Extract a single matching IA2 label from text. + + Args: + text (Any): Input text containing IA2 label content. + labels (list[str]): Valid label patterns to match. + + Returns: + str | float: The matched label when unique, otherwise `np.nan`. + """ if not isinstance(text, str): return np.nan @@ -27,6 +47,15 @@ def norm_ia2_label(text, labels: list[str]) -> str | float: def label_to_conll_format(labels: pd.Series) -> pd.Series: + """ + Convert label sequences to CONLL BIO tags. + + Args: + labels (pd.Series): Sequence of labels. + + Returns: + pd.Series: Labels converted to BIO format. + """ labels = labels.copy() if len(labels.dropna()) == 0: @@ -43,6 +72,15 @@ def label_to_conll_format(labels: pd.Series) -> pd.Series: def ia2_text_preprocess(text: str) -> str: + """ + Insert whitespace after IA2 XML-like tags when missing. + + Args: + text (str): Input text. + + Returns: + str: Preprocessed text. + """ text = re.sub(r"(<\w+>)(\S)", r"\g<1> \g<2>", text) return text @@ -52,7 +90,18 @@ def mapping2conll( filename: str, text_column: str = "original", label_column: str = "label", -): +) -> None: + """ + Write token-label mappings to a CONLL-style file. + + Args: + df (pd.DataFrame): DataFrame containing token and label columns. + filename (str): Output file path. + text_column (str, optional): Column name containing tokens. + Defaults to "original". + label_column (str, optional): Column name containing labels. + Defaults to "label". + """ dir = os.path.dirname(filename) os.makedirs(dir, exist_ok=True) with open(filename, "w") as file: diff --git a/aymurai/utils/cache.py b/aymurai/utils/cache.py index be653042..6d1928eb 100644 --- a/aymurai/utils/cache.py +++ b/aymurai/utils/cache.py @@ -21,7 +21,7 @@ def flatten_dict(current: dict, key: str = "", result: dict = {}) -> dict: Args: current (dict): Source dictionary to flatten. - key (str): Parent key prefix. Defaults to "". + key (str, optional): Parent key prefix. Defaults to "". result (dict, optional): Accumulator reused across recursion. Defaults to {}. Returns: @@ -53,7 +53,8 @@ def get_cache_key(item: Any, context: Any = "") -> str: Args: item (Any): Value to hash into the key namespace. - context (Any): Additional context used to scope the key. Defaults to "". + context (Any, optional): Additional context used to scope the key. + Defaults to "". Returns: str: Deterministic hash suitable for cache lookups. @@ -91,9 +92,6 @@ def cache_save(data_item: Any, key: str) -> None: Args: data_item (Any): Serializable payload to store. key (str): Cache key under which the payload is saved. - - Returns: - None: This function does not return a value. """ data = pickle.dumps(data_item) diff --git a/aymurai/utils/display/pandas.py b/aymurai/utils/display/pandas.py index 2f83742b..d2abc054 100644 --- a/aymurai/utils/display/pandas.py +++ b/aymurai/utils/display/pandas.py @@ -1,7 +1,18 @@ +from typing import ContextManager + import pandas as pd from more_itertools import flatten -def pandas_context(**kwargs): +def pandas_context(**kwargs) -> ContextManager[None]: + """ + Build a pandas option context manager from keyword options. + + Args: + **kwargs: Pandas option names mapped to values. + + Returns: + ContextManager[None]: Context manager that applies the provided options. + """ options = flatten(kwargs.items()) return pd.option_context(*options) # type: ignore diff --git a/aymurai/utils/download.py b/aymurai/utils/download.py index 9ce6ea6a..52d2a7f3 100644 --- a/aymurai/utils/download.py +++ b/aymurai/utils/download.py @@ -19,11 +19,14 @@ def download(url: str, output: str) -> str: Skips download when the target exists and NO_DOWNLOAD_IF_EXISTS is truthy. Args: - url: URL to download. - output: Path to save the downloaded file. + url (str): URL to download. + output (str): Path to save the downloaded file. Returns: str: Path to the downloaded file. + + Raises: + requests.HTTPError: If the download request returns an HTTP error status. """ output_path = Path(output) diff --git a/aymurai/utils/entity_disambiguation/core.py b/aymurai/utils/entity_disambiguation/core.py index 22982c6c..f12bedc4 100644 --- a/aymurai/utils/entity_disambiguation/core.py +++ b/aymurai/utils/entity_disambiguation/core.py @@ -58,10 +58,10 @@ def map_canonical_entities_ner_preds( Args: predictions (DocumentAnnotations): Original predictions to update. canonical_entities (CanonicalEntities): Canonical entities with IDs/roles. - include_label_instances (bool): Whether to assign ordered label instance - indices (e.g., 1, 2). Defaults to True. - force_labels (set[str] | None): Labels to remap even if a canonical ID - already exists. + include_label_instances (bool, optional): Whether to assign ordered label + instance indices (e.g., 1, 2). Defaults to True. + force_labels (set[str] | None, optional): Labels to remap even if a + canonical ID already exists. Defaults to None. Returns: DocumentAnnotations: Updated predictions with canonical IDs, roles, and diff --git a/aymurai/utils/entity_disambiguation/fuzzy.py b/aymurai/utils/entity_disambiguation/fuzzy.py index 71ceed76..682810b6 100644 --- a/aymurai/utils/entity_disambiguation/fuzzy.py +++ b/aymurai/utils/entity_disambiguation/fuzzy.py @@ -33,9 +33,6 @@ def _union_parent(parent: list[int], left: int, right: int) -> None: parent (list[int]): Parent pointers for the union-find structure. left (int): First index to union. right (int): Second index to union. - - Returns: - None """ root_left, root_right = _find_parent(parent, left), _find_parent(parent, right) if root_left != root_right: @@ -167,8 +164,8 @@ def build_canonical_entities( Args: labels (Iterable[DocLabel]): NER labels to cluster. - target_labels (set[str] | None): Optional label filter; if provided, - only these labels are clustered. + target_labels (set[str] | None, optional): Label filter; if provided, + only these labels are clustered. Defaults to None. threshold (int): Minimum similarity threshold for clustering. Returns: diff --git a/aymurai/utils/json_data.py b/aymurai/utils/json_data.py index 7fda48d0..b69bf5d6 100644 --- a/aymurai/utils/json_data.py +++ b/aymurai/utils/json_data.py @@ -9,7 +9,7 @@ def json_serial(obj: Any) -> str: JSON serializer for objects not serializable by default JSON encoder. Args: - obj: The object to serialize. This function currently supports + obj (Any): The object to serialize. This function currently supports datetime.date and datetime.datetime objects. Returns: @@ -17,7 +17,7 @@ def json_serial(obj: Any) -> str: Raises: TypeError: If the object is not of type datetime.date or datetime.datetime. - """ # noqa: E501 + """ if isinstance(obj, (datetime, date)): return obj.isoformat() raise TypeError(f"Type {type(obj)} not serializable") @@ -28,17 +28,16 @@ def get_pretty(obj: dict | list[Any]) -> str: Returns a pretty json string. Args: - obj (Union[dict, list[Any]]): the object to be converted to json. + obj (dict | list[Any]): The object to be converted to JSON. Returns: - str: the pretty json string. + str: The pretty JSON string. """ return json.dumps(obj, indent=4, ensure_ascii=False, default=json_serial) def save_json(json_data: dict | list[dict], file_path: str) -> None: """ - Saves a JSON object to a file. Args: diff --git a/aymurai/utils/json_encoding.py b/aymurai/utils/json_encoding.py index c4b2e930..5ff367a9 100644 --- a/aymurai/utils/json_encoding.py +++ b/aymurai/utils/json_encoding.py @@ -3,6 +3,7 @@ import json import decimal import datetime +from typing import Any import pandas as pd @@ -12,7 +13,22 @@ class EnhancedJSONEncoder(json.JSONEncoder): - def default(self, obj): + """JSON encoder with support for datetime, timedelta, decimal, and NA values.""" + + def default(self, obj: Any) -> Any: + """ + Encode unsupported Python objects into JSON-serializable payloads. + + Args: + obj (Any): Object to encode. + + Returns: + Any: JSON-serializable representation of `obj`. + + Raises: + TypeError: If `obj` cannot be encoded by the custom logic or parent + JSON encoder. + """ if isinstance(obj, datetime.datetime): ARGS = ("year", "month", "day", "hour", "minute", "second", "microsecond") return { @@ -55,10 +71,28 @@ def default(self, obj): class EnhancedJSONDecoder(json.JSONDecoder): - def __init__(self, *args, **kwargs): + """JSON decoder that reconstructs objects serialized by `EnhancedJSONEncoder`.""" + + def __init__(self, *args, **kwargs) -> None: + """ + Initialize decoder with a custom object hook. + + Args: + *args: Positional arguments forwarded to `json.JSONDecoder`. + **kwargs: Keyword arguments forwarded to `json.JSONDecoder`. + """ super().__init__(*args, object_hook=self.object_hook, **kwargs) - def object_hook(self, d): + def object_hook(self, d: dict) -> Any: + """ + Decode typed payloads into native Python objects. + + Args: + d (dict): Decoded dictionary from JSON. + + Returns: + Any: Reconstructed object when `__type__` is present, else `d`. + """ if "__type__" not in d: return d o = sys.modules[__name__] diff --git a/aymurai/utils/misc.py b/aymurai/utils/misc.py index 286d30f4..6d665e80 100644 --- a/aymurai/utils/misc.py +++ b/aymurai/utils/misc.py @@ -1,25 +1,31 @@ import re -from typing import Any, Union +from typing import Any def get_element( - obj, - levels: Union[list, Any] = [], + obj: Any, + levels: list[Any] | Any = [], default: Any = None, *, ignore_errors: bool = True, -): +) -> Any: """ - retrieve element hierarchically + Retrieve an element from a nested object using hierarchical keys. Args: - obj (object): parent object to retrieve to - levels (Union[list, Any], optional): hierarchy levels. Defaults to []. - default (Any, optional): default value to return - ignore_errors (str, optional): raise errors or ignore them. Defaults to True. + obj (Any): Parent object to traverse. + levels (list[Any] | Any, optional): Hierarchy levels to access. Defaults to []. + default (Any, optional): Value returned when traversal fails and + `ignore_errors` is True. Defaults to None. + ignore_errors (bool, optional): Whether to suppress lookup errors. + Defaults to True. Returns: - _type_: element or None (in case child element doesnt exist and `ignore_errors=True`) + Any: Retrieved element, or `default` when traversal fails and + `ignore_errors` is True. + + Raises: + Exception: Propagates the underlying error when `ignore_errors` is False. """ # if levels not a list handle it has a key @@ -40,7 +46,16 @@ def get_element( raise -def is_url(text: str): +def is_url(text: str) -> bool: + """ + Check whether a string contains a URL-like pattern. + + Args: + text (str): Text to evaluate. + + Returns: + bool: True when a URL-like pattern is found, otherwise False. + """ match = re.findall( r"(http(s)?:\/\/.)(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)", text, @@ -49,11 +64,16 @@ def is_url(text: str): # Taken from https://stackoverflow.com/a/20254842 -def get_recursively(search_dict: dict, field: str) -> list: +def get_recursively(search_dict: dict, field: str) -> list[Any]: """ - Takes a dict with nested lists and dicts, - and searches all dicts for a key of the field - provided. + Search nested dictionaries and lists for values under a target key. + + Args: + search_dict (dict): Dictionary to search recursively. + field (str): Key name to collect. + + Returns: + list[Any]: Values found for `field` across the nested structure. """ fields_found = [] diff --git a/aymurai/utils/pickle_data.py b/aymurai/utils/pickle_data.py index 4d5ceebb..09169ab1 100644 --- a/aymurai/utils/pickle_data.py +++ b/aymurai/utils/pickle_data.py @@ -3,12 +3,28 @@ from typing import Any -def save_pickle(object_: Any, output_path: str): +def save_pickle(object_: Any, output_path: str) -> None: + """ + Serialize and save a Python object to a pickle file. + + Args: + object_ (Any): Object to serialize. + output_path (str): Output file path. + """ with open(output_path, "wb") as f: pickle.dump(object_, f) def load_pickle(input_path: str) -> Any: + """ + Load and deserialize a Python object from a pickle file. + + Args: + input_path (str): Input file path. + + Returns: + Any: Deserialized Python object. + """ with (open(input_path, "rb")) as f: object_ = pickle.load(f) return object_ From 4559764c87b394381b97d061cd438e0b4c311fc3 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Fri, 20 Feb 2026 15:20:09 -0300 Subject: [PATCH 079/101] =?UTF-8?q?=F0=9F=94=A5=20Removed=20PromptLibrary?= =?UTF-8?q?=20class=20from=20aymurai/api/endpoints/routers/anonymizer/anon?= =?UTF-8?q?ymizer.py=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔥 Removed `llm` disambiguation label policy for release/v1.5.0 compatibility --- aymurai/api/endpoints/routers/anonymizer/anonymizer.py | 7 ------- docs/anonymization/README.md | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index c90da84c..6a07fea4 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -27,7 +27,6 @@ DocumentAnnotations, DocumentInformation, LabelPolicy, - PromptLibrary, RenderPolicy, TextRequest, ) @@ -346,12 +345,6 @@ async def anonymizer_disambiguate( "List of per-paragraph predictions returned by /anonymizer/predict." ), ), - custom_prompts: PromptLibrary = Body( - default_factory=PromptLibrary, - description=( - "Set of prompts, user and system, for each label if it is provided." - ), - ), label_policies: dict[str, LabelPolicy] | None = Body( None, diff --git a/docs/anonymization/README.md b/docs/anonymization/README.md index 19603da6..4632d900 100644 --- a/docs/anonymization/README.md +++ b/docs/anonymization/README.md @@ -30,7 +30,7 @@ Se incorpora un esquema de políticas por etiqueta que permite decidir: - `anonymize`: `true | false` -- `disambiguation`: `"none" | "fuzzy" | "llm"` +- `disambiguation`: `"none" | "fuzzy"` - `use_subclass_when_available`: `true | false` Estas políticas pueden venir de: From 35412a47e5db94312ec2ce51b3c0b144300a0fb2 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Fri, 20 Feb 2026 16:02:36 -0300 Subject: [PATCH 080/101] =?UTF-8?q?=F0=9F=8E=A8=20Changed=20map=5Fcanonica?= =?UTF-8?q?l=5Fentities=5Fner=5Fpreds=20function=20in=20aymurai/utils/enti?= =?UTF-8?q?ty=5Fdisambiguation/core.py=20discarding=20the=20role=20assignm?= =?UTF-8?q?ent=20for=20release/v1.5.0=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎨 Changed aymurai/api/endpoints/routers/anonymizer/anonymizer.py discarding all the validations that had to do with LLM disambiguation for release/v1.5.0 compatibility 🎨 Minor changes in the rest of documents regarding to experimentation with the release/v1.5.0 API --- .../routers/anonymizer/anonymizer.py | 39 +- aymurai/meta/entities.py | 4 +- aymurai/utils/entity_disambiguation/core.py | 68 +-- docs/anonymization/README.md | 6 +- .../10-anonymize-document-render-policy.ipynb | 48 +- uv.lock | 422 ------------------ 6 files changed, 58 insertions(+), 529 deletions(-) diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 6a07fea4..5d9b3cdc 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -352,29 +352,16 @@ async def anonymizer_disambiguate( "Optional per-label policy overrides for disambiguation/anonymization." ), ), - target_labels: list[str] - | None = Query( - None, - description=( - "Optional label filter for LLM refinement (e.g., PER,DNI). " - "Fuzzy clustering still runs across all detected labels." - ), - ), session: Session = Depends(get_session), ) -> DocumentAnnotations: """ - Performs canonical entity disambiguation using fuzzy matching and LLM refinement. + Performs canonical entity disambiguation using fuzzy matching. Args: paragraphs: A list of DocumentInformation objects containing the NER predictions per paragraph that need to be disambiguated. - custom_prompts: A PromptLibrary object containing optional system and - user prompts. If not provided, the service uses the default prompts - configured in the environment. label_policies: Optional per-label disambiguation/anonymization policies. - target_labels: An optional list of entity labels to refine via LLM - (e.g., ["PER", "DNI"]). Fuzzy clustering still runs across all - detected labels. + session: Database session dependency for caching results. Returns: DocumentAnnotations: The original annotations enriched with 'canonical_entity_id' and 'role' fields for each resolved mention. @@ -397,41 +384,28 @@ async def anonymizer_disambiguate( and effective_label_policies.get(label.attrs.aymurai_label).anonymize } - default_llm_labels = target_labels if target_labels else [] - - llm_labels: list[str] = [] fuzzy_labels: set[str] = set() for label in all_detected_labels: policy = effective_label_policies.get(label) - if policy and policy.disambiguation == "llm": - llm_labels.append(label) - fuzzy_labels.add(label) - continue if policy and policy.disambiguation == "fuzzy": fuzzy_labels.add(label) continue if policy and policy.disambiguation == "none": continue - if label in default_llm_labels: - llm_labels.append(label) - fuzzy_labels.add(label) - else: - fuzzy_labels.add(label) + fuzzy_labels.add(label) effective_disambiguation_by_label: dict[str, str] = {} for label in all_detected_labels: - if label in llm_labels: - effective_disambiguation_by_label[label] = "llm" - elif label in fuzzy_labels: + if label in fuzzy_labels: effective_disambiguation_by_label[label] = "fuzzy" else: effective_disambiguation_by_label[label] = "none" logger.info( - "disambiguation targets: detected=%s llm=%s", + "disambiguation targets: detected=%s fuzzy=%s", sorted(all_detected_labels), - llm_labels, + sorted(fuzzy_labels), ) canonical_entities = ( @@ -461,7 +435,6 @@ async def anonymizer_disambiguate( predictions = map_canonical_entities_ner_preds( predictions=paragraphs, canonical_entities=canonical_entities, - force_labels=set(llm_labels), ) for document in predictions: diff --git a/aymurai/meta/entities.py b/aymurai/meta/entities.py index 759167c2..56393e29 100644 --- a/aymurai/meta/entities.py +++ b/aymurai/meta/entities.py @@ -38,9 +38,7 @@ class EntityAttributes(BaseModel): ) aymurai_disambiguation: str | None = Field( None, - description=( - "Override disambiguation mode for this entity (none, fuzzy, llm)." - ), + description=("Override disambiguation mode for this entity (none, fuzzy)."), ) aymurai_anonymize: bool | None = Field( None, diff --git a/aymurai/utils/entity_disambiguation/core.py b/aymurai/utils/entity_disambiguation/core.py index f12bedc4..ffa156fc 100644 --- a/aymurai/utils/entity_disambiguation/core.py +++ b/aymurai/utils/entity_disambiguation/core.py @@ -50,7 +50,6 @@ def map_canonical_entities_ner_preds( canonical_entities: CanonicalEntities, *, include_label_instances: bool = True, - force_labels: set[str] | None = None, ) -> DocumentAnnotations: """ Applies canonical entity IDs and roles back onto NER predictions. @@ -60,15 +59,12 @@ def map_canonical_entities_ner_preds( canonical_entities (CanonicalEntities): Canonical entities with IDs/roles. include_label_instances (bool, optional): Whether to assign ordered label instance indices (e.g., 1, 2). Defaults to True. - force_labels (set[str] | None, optional): Labels to remap even if a - canonical ID already exists. Defaults to None. Returns: DocumentAnnotations: Updated predictions with canonical IDs, roles, and optionally `aymurai_label_instance`. """ predictions_mapped = copy.deepcopy(predictions) - force_labels = force_labels or set() new_ids_map = {} @@ -80,47 +76,31 @@ def map_canonical_entities_ner_preds( if not label.attrs: continue - if label.attrs.aymurai_label_subclass is None: - label.attrs.aymurai_label_subclass = [] - - force_remap = label.attrs.aymurai_label in force_labels - if force_remap: - label.attrs.canonical_entity_id = None - label.attrs.aymurai_label_subclass = [] - - if ( - label.attrs.canonical_entity_id is None - and len(label.attrs.aymurai_label_subclass) == 0 - ): - for ce in canonical_entities: - if label.attrs.aymurai_label == ce.aymurai_label: - entity_id = ce.entity_id - attributes = ce.attributes or {} - role = attributes.get("role") - aliases = ce.aliases - - clean_aliases = [str(a).strip().lower() for a in aliases] - label_text = ( - str(label.attrs.aymurai_alt_text or label.text) - .strip() - .lower() - ) - - if label_text in clean_aliases: - label.attrs.canonical_entity_id = entity_id - if ce.aymurai_label == "PER" and role is not None: - label.attrs.aymurai_label_subclass.append(role) - break - - elif label.attrs.canonical_entity_id is None: - key = ( - label.attrs.aymurai_label, - str(label.attrs.aymurai_alt_text).strip(), - ) - if key not in new_ids_map: - new_ids_map[key] = uuid.uuid4() + for ce in canonical_entities: + if label.attrs.aymurai_label == ce.aymurai_label: + entity_id = ce.entity_id + aliases = ce.aliases + + clean_aliases = [str(a).strip().lower() for a in aliases] + label_text = ( + str(label.attrs.aymurai_alt_text or label.text).strip().lower() + ) + + if label_text in clean_aliases: + label.attrs.canonical_entity_id = entity_id + break + + if label.attrs.canonical_entity_id is not None: + continue + + key = ( + label.attrs.aymurai_label, + str(label.attrs.aymurai_alt_text).strip(), + ) + if key not in new_ids_map: + new_ids_map[key] = uuid.uuid4() - label.attrs.canonical_entity_id = new_ids_map[key] + label.attrs.canonical_entity_id = new_ids_map[key] if include_label_instances: return assign_label_instances(predictions_mapped) diff --git a/docs/anonymization/README.md b/docs/anonymization/README.md index 4632d900..29deee42 100644 --- a/docs/anonymization/README.md +++ b/docs/anonymization/README.md @@ -21,8 +21,8 @@ - `canonical_entity_id`: UUID de la entidad canónica asignada a cada mención. - `aymurai_label_instance`: índice entero por orden de aparición del `canonical_entity_id` dentro de un mismo label (1, 2, 3...). -- `aymurai_label_subclass`: lista de roles inferidos por LLM (p. ej. "Denunciante", "Juez/a"). -- `aymurai_disambiguation`: método aplicado para la desambiguación efectiva del label (`llm`, `fuzzy`, `none`). +- `aymurai_label_subclass`: en el caso de fechas donde encuentra el patrón se escribe la fecha en formato DD/MM/YYYY. +- `aymurai_disambiguation`: método aplicado para la desambiguación efectiva del label (`fuzzy`, `none`). - `aymurai_anonymize`: flag efectivo de anonimización (True/False). ### Políticas por label (nueva interfaz) @@ -59,7 +59,7 @@ El `render_policy` puede definirse por entorno (`RENDER_POLICY`) o por request y - **Nuevo input opcional**: `label_policies`. - **Nuevo output**: `label_policies` y metadatos efectivos en cada `DocLabel` (`aymurai_disambiguation`, `aymurai_anonymize`). -- La selección de labels para LLM/fuzzy se define por políticas, no por un modo global. +- La selección de labels para fuzzy se define por políticas, no por un modo global. ### `/anonymizer/anonymize-document` diff --git a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb index 0a5b279e..a9949050 100644 --- a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb +++ b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb @@ -64,7 +64,7 @@ "\n", "print(f\"Found {len(documents)} documents\")\n", "\n", - "doc_path = documents[16]" + "doc_path = documents[1]" ] }, { @@ -315,7 +315,7 @@ " with open(out_path, \"wb\") as file:\n", " file.write(response.content)\n", "\n", - " return out_path" + " return disambiguated, out_path" ] }, { @@ -337,33 +337,33 @@ " \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", " \"FECHA\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", "}\n", - "out_all_fuzzy = disambiguate_and_export(\n", + "disambiguated_labels_fuzzy, out_all_fuzzy = disambiguate_and_export(\n", " \"all-fuzzy\", label_policies_all_fuzzy, render_policy\n", ")\n", "\n", - "# 2) FECHAs excluded\n", - "label_policies_no_fecha = {\n", - " \"PER\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": True},\n", - " \"DNI\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"LOC\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"FECHA\": {\"disambiguation\": \"none\", \"anonymize\": False, \"use_subclass_when_available\": False},\n", - "}\n", - "out_no_fecha = disambiguate_and_export(\n", - " \"no-fecha\", label_policies_no_fecha, render_policy\n", - ")\n", + "# # 2) FECHAs excluded\n", + "# label_policies_no_fecha = {\n", + "# \"PER\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": True},\n", + "# \"DNI\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + "# \"LOC\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + "# \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + "# \"FECHA\": {\"disambiguation\": \"none\", \"anonymize\": False, \"use_subclass_when_available\": False},\n", + "# }\n", + "# disambiguated_labels_no_fecha, out_no_fecha = disambiguate_and_export(\n", + "# \"no-fecha\", label_policies_no_fecha, render_policy\n", + "# )\n", "\n", - "# 3) PER llm-disambiguated\n", - "label_policies_per_llm = {\n", - " \"PER\": {\"disambiguation\": \"llm\", \"anonymize\": False, \"use_subclass_when_available\": True},\n", - " \"DNI\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"LOC\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"FECHA\": {\"disambiguation\": \"none\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - "}\n", - "out_per_llm = disambiguate_and_export(\"per-llm\", label_policies_per_llm, render_policy)\n", "\n", - "out_all_fuzzy, out_no_fecha, out_per_llm" + "# out_all_fuzzy, out_no_fecha" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "disambiguated_labels_fuzzy" ] } ], diff --git a/uv.lock b/uv.lock index dbcc39c0..538334fc 100644 --- a/uv.lock +++ b/uv.lock @@ -98,22 +98,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554, upload-time = "2025-11-14T20:35:05.699Z" }, ] -[[package]] -name = "altair" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "narwhals" }, - { name = "packaging" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834, upload-time = "2025-11-12T08:59:11.519Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/33/ef2f2409450ef6daa61459d5de5c08128e7d3edb773fefd0a324d1310238/altair-6.0.0-py3-none-any.whl", hash = "sha256:09ae95b53d5fe5b16987dccc785a7af8588f2dca50de1e7a156efa8a461515f8", size = 795410, upload-time = "2025-11-12T08:59:09.804Z" }, -] - [[package]] name = "annotated-doc" version = "0.0.4" @@ -236,18 +220,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, ] -[[package]] -name = "asyncer" -version = "0.0.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217, upload-time = "2024-08-24T23:15:36.449Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209, upload-time = "2024-08-24T23:15:35.317Z" }, -] - [[package]] name = "attrs" version = "25.4.0" @@ -299,17 +271,14 @@ dependencies = [ [package.dev-dependencies] dev = [ - { name = "dspy" }, { name = "jupyter" }, { name = "matplotlib" }, { name = "nbstripout" }, { name = "pip" }, - { name = "playwright" }, { name = "plotly" }, { name = "pre-commit" }, { name = "rich" }, { name = "seaborn" }, - { name = "streamlit" }, ] [package.metadata] @@ -352,17 +321,14 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "dspy", specifier = ">=3.0.4" }, { name = "jupyter", specifier = ">=1.1.1" }, { name = "matplotlib", specifier = ">=3.10.0" }, { name = "nbstripout", specifier = ">=0.8.0" }, { name = "pip", specifier = ">=24.3.1" }, - { name = "playwright", specifier = "==1.56.0" }, { name = "plotly", specifier = ">=5.24.1" }, { name = "pre-commit", specifier = ">=4.0.1" }, { name = "rich", specifier = ">=13.9.4" }, { name = "seaborn", specifier = ">=0.13.2" }, - { name = "streamlit", specifier = ">=1.21.0" }, ] [[package]] @@ -420,15 +386,6 @@ css = [ { name = "tinycss2" }, ] -[[package]] -name = "blinker" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, -] - [[package]] name = "boto3" version = "1.42.23" @@ -544,15 +501,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -562,18 +510,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "colorlog" -version = "6.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, -] - [[package]] name = "comm" version = "0.2.3" @@ -725,15 +661,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - [[package]] name = "dnspython" version = "2.8.0" @@ -758,35 +685,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025, upload-time = "2025-03-24T20:59:24.394Z" }, ] -[[package]] -name = "dspy" -version = "3.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "asyncer" }, - { name = "cachetools" }, - { name = "cloudpickle" }, - { name = "diskcache" }, - { name = "gepa" }, - { name = "json-repair" }, - { name = "litellm" }, - { name = "numpy" }, - { name = "openai" }, - { name = "optuna" }, - { name = "orjson" }, - { name = "pydantic" }, - { name = "regex" }, - { name = "requests" }, - { name = "tenacity" }, - { name = "tqdm" }, - { name = "xxhash" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/bd/339e1d949aa24e50b6edca8574cf8afbff005a1dfadc2d5b82a0003fa7da/dspy-3.1.0.tar.gz", hash = "sha256:3c1660cb687411f064509cd7ac47ed0231761f50ed92823cbbb59b3af2885702", size = 242611, upload-time = "2026-01-06T18:50:18.655Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/34/64e900657cad3c4df8a417fccbc4370ca851864edbd0f468210f1b57d084/dspy-3.1.0-py3-none-any.whl", hash = "sha256:b9f4d42cad9ac32b13fdf085dd3246c8ee8339c759cb291c5e7b7f9f5fb5dd72", size = 291317, upload-time = "2026-01-06T18:50:17.559Z" }, -] - [[package]] name = "email-validator" version = "2.3.0" @@ -943,25 +841,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, ] -[[package]] -name = "fastuuid" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760, upload-time = "2025-10-19T22:25:21.509Z" }, - { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748, upload-time = "2025-10-19T22:41:52.873Z" }, - { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537, upload-time = "2025-10-19T22:33:55.603Z" }, - { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994, upload-time = "2025-10-19T22:26:17.631Z" }, - { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003, upload-time = "2025-10-19T22:23:45.415Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583, upload-time = "2025-10-19T22:26:00.756Z" }, - { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955, upload-time = "2025-10-19T22:36:25.196Z" }, - { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763, upload-time = "2025-10-19T22:24:28.451Z" }, - { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613, upload-time = "2025-10-19T22:25:06.827Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045, upload-time = "2025-10-19T22:28:32.732Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122, upload-time = "2025-10-19T22:23:15.59Z" }, -] - [[package]] name = "filelock" version = "3.20.2" @@ -1111,39 +990,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/70/e07c381e6488a77094f04c85c9caf1c8008cdc30778f7019bc52e5285ef0/gdown-5.2.0-py3-none-any.whl", hash = "sha256:33083832d82b1101bdd0e9df3edd0fbc0e1c5f14c9d8c38d2a35bf1683b526d6", size = 18235, upload-time = "2024-05-12T06:45:10.017Z" }, ] -[[package]] -name = "gepa" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/be/9a4c31c65c4910e73bd4a316df99347681bce4f2ef6d10877cc1ed8d57da/gepa-0.0.22.tar.gz", hash = "sha256:0b13a644efd2af52186e456eaad2ae28fcf88c6f796a9c999910c587863d4315", size = 116337, upload-time = "2025-11-10T21:39:27.044Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/0e/d41272b28f2163f5bf840b508cbaf4a0bc01eaeaa6e5d447558d1050eb3b/gepa-0.0.22-py3-none-any.whl", hash = "sha256:ff57ee0442399a5cc62d01411935476bc80cfa8f1c318bed82e3db4c2c834665", size = 119666, upload-time = "2025-11-10T21:39:26.028Z" }, -] - -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, -] - -[[package]] -name = "gitpython" -version = "3.1.46" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, -] - [[package]] name = "greenlet" version = "3.3.0" @@ -1264,18 +1110,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - [[package]] name = "intervaltree" version = "3.2.1" @@ -1386,26 +1220,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] -[[package]] -name = "jiter" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652, upload-time = "2025-11-09T20:46:41.021Z" }, - { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829, upload-time = "2025-11-09T20:46:43.281Z" }, - { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568, upload-time = "2025-11-09T20:46:45.075Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052, upload-time = "2025-11-09T20:46:46.818Z" }, - { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585, upload-time = "2025-11-09T20:46:48.319Z" }, - { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541, upload-time = "2025-11-09T20:46:49.643Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423, upload-time = "2025-11-09T20:46:51.731Z" }, - { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958, upload-time = "2025-11-09T20:46:53.432Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084, upload-time = "2025-11-09T20:46:54.848Z" }, - { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054, upload-time = "2025-11-09T20:46:56.487Z" }, - { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368, upload-time = "2025-11-09T20:46:58.638Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847, upload-time = "2025-11-09T20:47:00.295Z" }, -] - [[package]] name = "jiwer" version = "3.0.5" @@ -1437,15 +1251,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] -[[package]] -name = "json-repair" -version = "0.55.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/74/0f39677fa7c0127129c3f1a37c94d05c30a968ba3047200e54dea375b09a/json_repair-0.55.0.tar.gz", hash = "sha256:9fafb47d92582ef4bdd3520656bdb0fcb37b46cf6aa99c1926b7895abc0a3a4b", size = 38828, upload-time = "2026-01-01T20:29:02.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/00/d7d6b6b3257f9b1f997a558b6f7087b8af7c0a2f525f4fbd864c267a88ab/json_repair-0.55.0-py3-none-any.whl", hash = "sha256:bcf4880f5e6ad21a0f70ab034e3d1d398c2ae9698dc5717d7015afbac77b8ed7", size = 29570, upload-time = "2026-01-01T20:29:01.042Z" }, -] - [[package]] name = "json5" version = "0.13.0" @@ -1772,29 +1577,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594, upload-time = "2022-09-06T16:09:04.658Z" }, ] -[[package]] -name = "litellm" -version = "1.80.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fastuuid" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/8c/48d533affdbc6d485b7ad4221cd3b40b8c12f9f5568edfe0be0b11e7b945/litellm-1.80.0.tar.gz", hash = "sha256:eeac733eb6b226f9e5fb020f72fe13a32b3354b001dc62bcf1bc4d9b526d6231", size = 11591976, upload-time = "2025-11-16T00:03:51.812Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/53/aa31e4d057b3746b3c323ca993003d6cf15ef987e7fe7ceb53681695ae87/litellm-1.80.0-py3-none-any.whl", hash = "sha256:fd0009758f4772257048d74bf79bb64318859adb4ea49a8b66fdbc718cd80b6e", size = 10492975, upload-time = "2025-11-16T00:03:49.182Z" }, -] - [[package]] name = "lxml" version = "6.0.2" @@ -2297,64 +2079,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045, upload-time = "2020-01-18T16:55:48.852Z" } -[[package]] -name = "openai" -version = "1.109.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133, upload-time = "2025-09-24T13:00:53.075Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627, upload-time = "2025-09-24T13:00:50.754Z" }, -] - -[[package]] -name = "optuna" -version = "4.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alembic" }, - { name = "colorlog" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "sqlalchemy" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6b/81/08f90f194eed78178064a9383432eca95611e2c5331e7b01e2418ce4b15a/optuna-4.6.0.tar.gz", hash = "sha256:89e38c2447c7f793a726617b8043f01e31f0bad54855040db17eb3b49404a369", size = 477444, upload-time = "2025-11-10T05:14:30.151Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/de/3d8455b08cb6312f8cc46aacdf16c71d4d881a1db4a4140fc5ef31108422/optuna-4.6.0-py3-none-any.whl", hash = "sha256:4c3a9facdef2b2dd7e3e2a8ae3697effa70fae4056fcf3425cfc6f5a40feb069", size = 404708, upload-time = "2025-11-10T05:14:28.6Z" }, -] - -[[package]] -name = "orjson" -version = "3.11.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/b8/333fdb27840f3bf04022d21b654a35f58e15407183aeb16f3b41aa053446/orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5", size = 5972347, upload-time = "2025-12-06T15:55:39.458Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/19/b22cf9dad4db20c8737041046054cbd4f38bb5a2d0e4bb60487832ce3d76/orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1", size = 245719, upload-time = "2025-12-06T15:53:43.877Z" }, - { url = "https://files.pythonhosted.org/packages/03/2e/b136dd6bf30ef5143fbe76a4c142828b55ccc618be490201e9073ad954a1/orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870", size = 132467, upload-time = "2025-12-06T15:53:45.379Z" }, - { url = "https://files.pythonhosted.org/packages/ae/fc/ae99bfc1e1887d20a0268f0e2686eb5b13d0ea7bbe01de2b566febcd2130/orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09", size = 130702, upload-time = "2025-12-06T15:53:46.659Z" }, - { url = "https://files.pythonhosted.org/packages/6e/43/ef7912144097765997170aca59249725c3ab8ef6079f93f9d708dd058df5/orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd", size = 135907, upload-time = "2025-12-06T15:53:48.487Z" }, - { url = "https://files.pythonhosted.org/packages/3f/da/24d50e2d7f4092ddd4d784e37a3fa41f22ce8ed97abc9edd222901a96e74/orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac", size = 139935, upload-time = "2025-12-06T15:53:49.88Z" }, - { url = "https://files.pythonhosted.org/packages/02/4a/b4cb6fcbfff5b95a3a019a8648255a0fac9b221fbf6b6e72be8df2361feb/orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e", size = 137541, upload-time = "2025-12-06T15:53:51.226Z" }, - { url = "https://files.pythonhosted.org/packages/a5/99/a11bd129f18c2377c27b2846a9d9be04acec981f770d711ba0aaea563984/orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f", size = 139031, upload-time = "2025-12-06T15:53:52.309Z" }, - { url = "https://files.pythonhosted.org/packages/64/29/d7b77d7911574733a036bb3e8ad7053ceb2b7d6ea42208b9dbc55b23b9ed/orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18", size = 141622, upload-time = "2025-12-06T15:53:53.606Z" }, - { url = "https://files.pythonhosted.org/packages/93/41/332db96c1de76b2feda4f453e91c27202cd092835936ce2b70828212f726/orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a", size = 413800, upload-time = "2025-12-06T15:53:54.866Z" }, - { url = "https://files.pythonhosted.org/packages/76/e1/5a0d148dd1f89ad2f9651df67835b209ab7fcb1118658cf353425d7563e9/orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7", size = 151198, upload-time = "2025-12-06T15:53:56.383Z" }, - { url = "https://files.pythonhosted.org/packages/0d/96/8db67430d317a01ae5cf7971914f6775affdcfe99f5bff9ef3da32492ecc/orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401", size = 141984, upload-time = "2025-12-06T15:53:57.746Z" }, - { url = "https://files.pythonhosted.org/packages/71/49/40d21e1aa1ac569e521069228bb29c9b5a350344ccf922a0227d93c2ed44/orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8", size = 135272, upload-time = "2025-12-06T15:53:59.769Z" }, - { url = "https://files.pythonhosted.org/packages/c4/7e/d0e31e78be0c100e08be64f48d2850b23bcb4d4c70d114f4e43b39f6895a/orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167", size = 133360, upload-time = "2025-12-06T15:54:01.25Z" }, -] - [[package]] name = "overrides" version = "7.7.0" @@ -2468,25 +2192,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, ] -[[package]] -name = "playwright" -version = "1.56.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet" }, - { name = "pyee" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424, upload-time = "2025-11-11T18:39:10.175Z" }, - { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228, upload-time = "2025-11-11T18:39:13.915Z" }, - { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424, upload-time = "2025-11-11T18:39:17.093Z" }, - { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122, upload-time = "2025-11-11T18:39:20.619Z" }, - { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645, upload-time = "2025-11-11T18:39:24.005Z" }, - { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837, upload-time = "2025-11-11T18:39:27.174Z" }, - { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843, upload-time = "2025-11-11T18:39:30.851Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959, upload-time = "2025-11-11T18:39:33.998Z" }, -] - [[package]] name = "plotly" version = "6.5.0" @@ -2718,31 +2423,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, ] -[[package]] -name = "pydeck" -version = "0.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, -] - -[[package]] -name = "pyee" -version = "13.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, -] - [[package]] name = "pygments" version = "2.19.2" @@ -3342,24 +3022,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "smmap" -version = "5.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, -] - [[package]] name = "sortedcontainers" version = "2.4.0" @@ -3444,35 +3106,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, ] -[[package]] -name = "streamlit" -version = "1.52.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "altair" }, - { name = "blinker" }, - { name = "cachetools" }, - { name = "click" }, - { name = "gitpython" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "pillow" }, - { name = "protobuf" }, - { name = "pyarrow" }, - { name = "pydeck" }, - { name = "requests" }, - { name = "tenacity" }, - { name = "toml" }, - { name = "tornado" }, - { name = "typing-extensions" }, - { name = "watchdog", marker = "sys_platform != 'darwin'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/20/434aaceccc6e1912671d869926103051330437adba72d538d787a07727ef/streamlit-1.52.2.tar.gz", hash = "sha256:64a4dda8bc5cdd37bfd490e93bb53da35aaef946fcfc283a7980dacdf165108b", size = 8584178, upload-time = "2025-12-17T17:07:59.642Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/95/6b7873f0267973ebd55ba9cd33a690b35a116f2779901ef6185a0e21864d/streamlit-1.52.2-py3-none-any.whl", hash = "sha256:a16bb4fbc9781e173ce9dfbd8ffb189c174f148f9ca4fb8fa56423e84e193fc8", size = 9025937, upload-time = "2025-12-17T17:07:57.67Z" }, -] - [[package]] name = "sympy" version = "1.14.0" @@ -3549,25 +3182,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991, upload-time = "2025-10-06T20:21:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798, upload-time = "2025-10-06T20:21:35.579Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865, upload-time = "2025-10-06T20:21:36.675Z" }, - { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856, upload-time = "2025-10-06T20:21:37.873Z" }, - { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308, upload-time = "2025-10-06T20:21:39.577Z" }, - { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697, upload-time = "2025-10-06T20:21:41.154Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375, upload-time = "2025-10-06T20:21:43.201Z" }, -] - [[package]] name = "tinycss2" version = "1.4.0" @@ -3610,15 +3224,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, ] -[[package]] -name = "toml" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, -] - [[package]] name = "tomli" version = "2.3.0" @@ -3895,24 +3500,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, ] -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - [[package]] name = "watchfiles" version = "1.1.1" @@ -4102,12 +3689,3 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] From 014b28e115a43d4d6b656d25554ecde35646642d Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 19:50:49 +0000 Subject: [PATCH 081/101] =?UTF-8?q?=F0=9F=94=80=20Synthesize=20document=5F?= =?UTF-8?q?extract=20from=20d349c69=20after=203c55d8e:=20remove=20extracto?= =?UTF-8?q?r=20config=20passthrough=20and=20restore=20fixed=20timeout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routers/misc/document_extract.py | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/aymurai/api/endpoints/routers/misc/document_extract.py b/aymurai/api/endpoints/routers/misc/document_extract.py index 11354a0e..37b7d0a4 100644 --- a/aymurai/api/endpoints/routers/misc/document_extract.py +++ b/aymurai/api/endpoints/routers/misc/document_extract.py @@ -19,32 +19,24 @@ router = APIRouter() -def extraction( - path: str, - use_cache: bool = True, - **kwargs, -) -> str: +def extraction(path: str) -> str: """ Wrapper function to call the extract_document function. This is necessary to ensure that the function can be pickled and run in a separate process. Args: path (str): Path to the file to be processed. - use_cache (bool): Whether to use caching for the extraction. - **kwargs: Extractor-specific configuration overrides. Returns: str: Extracted text from the document. """ - text = extract_document(path, use_cache=use_cache, **kwargs) + text = extract_document(path) return document_normalize(text) if text else "" def run_safe_text_extraction( path: str, - timeout_s: float | None = None, - use_cache: bool = True, - **kwargs, + timeout_s: float | None = 30, ) -> str: """ Runs the text extraction in a separate process to avoid blocking the main thread. @@ -53,9 +45,7 @@ def run_safe_text_extraction( Args: path (str): Path to the file to be processed. timeout_s (float | None): Timeout in seconds for the extraction process. - If None, waits indefinitely. Defaults to None. - use_cache (bool): Whether to use caching for the extraction. - **kwargs: Extractor-specific configuration overrides. + If None, waits indefinitely. Defaults to 30. Returns: str: Extracted text from the document. @@ -64,7 +54,7 @@ def run_safe_text_extraction( TimeoutError: If the extraction process exceeds the specified timeout. """ with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor: - future = executor.submit(extraction, path, use_cache, **kwargs) + future = executor.submit(extraction, path) try: return future.result(timeout=timeout_s) except concurrent.futures.TimeoutError: @@ -74,20 +64,12 @@ def run_safe_text_extraction( @router.post("/document-extract", response_model=Document) -def plain_text_extractor( - file: UploadFile, - use_cache: bool = True, - y_tolerance: float | None = None, - debug: bool | None = None, -) -> Document: +def plain_text_extractor(file: UploadFile) -> Document: """ Extract plain text from an uploaded document. Args: file (UploadFile): Incoming document upload. - use_cache (bool): Whether to use caching for the extraction. Defaults to True. - y_tolerance (float | None): Optional vertical tolerance for PDF paragraph merging. Defaults to None. - debug (bool | None): Optional override for marker debug mode. Defaults to None. Returns: Document: Extracted and normalized document payload. @@ -109,12 +91,7 @@ def plain_text_extractor( logger.info(f"saved temp file on local storage => {tmp_filename}") - document = run_safe_text_extraction( - tmp_filename, - use_cache=use_cache, - y_tolerance=y_tolerance, - debug=debug, - ) + document = run_safe_text_extraction(tmp_filename) except concurrent.futures.TimeoutError: logger.error(f"Timeout while extracting text from {file.filename}") From 91d2c100c221e2e0bb30539ba9c826c45ac1df5a Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 19:51:25 +0000 Subject: [PATCH 082/101] =?UTF-8?q?=F0=9F=94=80=20Synthesize=20PDF=20extra?= =?UTF-8?q?ction=20flow=20from=20d349c69/26033a8:=20remove=20cache/debug?= =?UTF-8?q?=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/text/extractors/pdf.py | 72 ++++---------------------------- aymurai/text/extractors/utils.py | 21 ++++------ 2 files changed, 15 insertions(+), 78 deletions(-) diff --git a/aymurai/text/extractors/pdf.py b/aymurai/text/extractors/pdf.py index 19cec025..11708e2e 100644 --- a/aymurai/text/extractors/pdf.py +++ b/aymurai/text/extractors/pdf.py @@ -1,12 +1,7 @@ -from hashlib import blake2b from pathlib import Path -from aymurai.logger import get_logger from aymurai.text.extractors.base import BaseExtractor, InvalidFile, register_extractor from aymurai.text.extractors.utils import pdf_to_text -from aymurai.utils.cache import cache_load, cache_save, get_cache_key - -logger = get_logger(__file__) @register_extractor @@ -17,80 +12,27 @@ def extract( self, path: Path, y_tolerance: float | None = None, - *, - use_cache: bool = True, - debug: bool | None = None, ) -> str: """ Extract normalized text from a PDF document. Args: path (Path): Input document path. - use_cache (bool): Toggle extractor-level caching. Defaults to True. - debug (bool | None): Optional override for marker debug mode. Defaults to None. + y_tolerance (float | None, optional): Maximum vertical gap used to + merge nearby text blocks. If None, it is estimated from the + document. Defaults to None. Returns: str: Cleaned textual content. + + Raises: + InvalidFile: If the file is unreadable or extraction fails. """ file_path = self.ensure_file(path) - # Check cache first when enabled - cache_key = self._cache_key(file_path) if use_cache else None - if use_cache and cache_key: - cached_text = cache_load(cache_key) - if cached_text is not None: - logger.debug("PDF cache hit for %s", file_path) - return cached_text - try: - text = pdf_to_text( - file_path, - y_tolerance=y_tolerance, - debug=debug, - ) + return pdf_to_text(file_path, y_tolerance=y_tolerance) except (OSError, ValueError) as exc: raise InvalidFile(str(exc)) from exc except Exception as exc: raise InvalidFile(str(exc)) from exc - if use_cache and cache_key: - cache_save(text, key=cache_key) - logger.debug("PDF cache stored for %s", file_path) - - return text - - @staticmethod - def _cache_key(file_path: Path) -> str | None: - """ - Compute a stable cache key for the PDF payload. - - Args: - file_path (Path): Location of the PDF file to fingerprint. - - Returns: - str | None: Deterministic cache key, or ``None`` when the file is unreadable. - """ - try: - stat = file_path.stat() - except OSError as exc: - logger.warning("Unable to stat PDF %s for caching: %s", file_path, exc) - return None - - try: - hasher = blake2b(digest_size=32) - with file_path.open("rb") as handle: - for chunk in iter(lambda: handle.read(65536), b""): - hasher.update(chunk) - fingerprint = hasher.hexdigest() - except OSError as exc: - logger.warning("Unable to hash PDF %s for caching: %s", file_path, exc) - fingerprint = None - - item = fingerprint or file_path.resolve().as_posix() - context = { - "component": "pdf-extractor", - "size": stat.st_size, - } - if fingerprint is None: - context["mtime_ns"] = stat.st_mtime_ns - - return get_cache_key(item, context=context) diff --git a/aymurai/text/extractors/utils.py b/aymurai/text/extractors/utils.py index 881b0633..009b562d 100644 --- a/aymurai/text/extractors/utils.py +++ b/aymurai/text/extractors/utils.py @@ -1,26 +1,22 @@ +import statistics import unicodedata import xml.etree.ElementTree as ET import zipfile from pathlib import Path from typing import Any +import numpy as np +import pymupdf import xmltodict from lxml import etree from more_itertools import flatten -import pymupdf -import statistics -import numpy as np - from aymurai.logger import get_logger from aymurai.utils.misc import get_element, get_recursively logger = get_logger(__file__) -BLOCK_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "blockquote", "pre"} - - ODT_NS = {"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0"} @@ -75,12 +71,13 @@ def _compute_median_margin_between_blocks(pdf_path: str) -> float: return 0.0 # Return 0 if no margins were found -def _extract_and_merge_paragraphs(pdf_path: str, y_tolerance=5) -> list[str]: +def _extract_and_merge_paragraphs(pdf_path: str, y_tolerance: float = 5) -> list[str]: """ Extracts and merges paragraphs from a PDF by grouping close text blocks. Args: pdf_path (str): Path to the PDF file. - y_tolerance (float): Maximum vertical gap (in points) to consider blocks part of the same paragraph. + y_tolerance (float, optional): Maximum vertical gap (in points) to consider blocks part of the same paragraph. + Defaults to 5. Returns: list[str]: A list of merged paragraphs as strings. """ @@ -118,16 +115,14 @@ def _extract_and_merge_paragraphs(pdf_path: str, y_tolerance=5) -> list[str]: def pdf_to_text( file_path: Path | str, y_tolerance: float | None = None, - debug: bool | None = None, ) -> str: """ Extract text from a PDF file and return normalized plain text. Args: file_path (Path): Path to the PDF document. - y_tolerance (float, optional): - Maximum vertical gap (in points) to consider blocks part of the same paragraph. - debug (bool | None): Optional override for marker debug mode. Defaults to None. + y_tolerance (float, optional): Maximum vertical gap (in points) to consider blocks part of the same paragraph. + If None, it will be computed as the median margin between blocks. Defaults to None. Returns: str: Cleaned textual content extracted from the PDF. From 000215ed6b7d412aaf6528c62e2f5e4c855edfe9 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 19:52:41 +0000 Subject: [PATCH 083/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20text=20extracti?= =?UTF-8?q?on=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/text/test_extraction.py | 94 -------------------- test/text/test_extractors.py | 164 ----------------------------------- 2 files changed, 258 deletions(-) delete mode 100644 test/text/test_extraction.py delete mode 100644 test/text/test_extractors.py diff --git a/test/text/test_extraction.py b/test/text/test_extraction.py deleted file mode 100644 index 69df8f7e..00000000 --- a/test/text/test_extraction.py +++ /dev/null @@ -1,94 +0,0 @@ -import tempfile -import unittest -from pathlib import Path -from unittest.mock import patch - -from aymurai.text.extraction import ERRORS, InvalidFile, extract_document, get_extension - - -class ExtractionTestCase(unittest.TestCase): - def setUp(self) -> None: - self.tempdir = tempfile.TemporaryDirectory() - self.addCleanup(self.tempdir.cleanup) - self.tmp_path = Path(self.tempdir.name) - - def test_extract_document_dispatches(self): - source = self.tmp_path / "document.docx" - source.write_text("dummy") - - class DummyExtractor: - def __init__(self) -> None: - self.called = False - self.called_path: Path | None = None - - def extract(self, path: Path, **_: object) -> str: - self.called = True - self.called_path = path - return "ok" - - dummy = DummyExtractor() - - with patch( - "aymurai.text.extraction.get_extractor", - return_value=dummy, - ): - result = extract_document(source) - - self.assertEqual(result, "ok") - self.assertTrue(dummy.called) - self.assertEqual(dummy.called_path, source) - - def test_extract_document_missing_file_raises(self): - missing = Path("/no/such/file.pdf") - with self.assertRaises(InvalidFile): - extract_document(missing, errors="raise") - - def test_extract_document_handles_invalid_file(self): - source = self.tmp_path / "document.pdf" - source.write_text("dummy") - - class BoomExtractor: - def extract(self, _path: Path, **_: object) -> str: - raise InvalidFile("boom") - - with patch( - "aymurai.text.extraction.get_extractor", - return_value=BoomExtractor(), - ): - result = extract_document(source, errors="ignore") - - self.assertIsNone(result) - - def test_extract_document_raises_unexpected(self): - source = self.tmp_path / "document.odt" - source.write_text("dummy") - - class BoomExtractor: - def extract(self, _path: Path, **_: object) -> str: - raise RuntimeError("boom") - - with patch( - "aymurai.text.extraction.get_extractor", - return_value=BoomExtractor(), - ): - with self.assertRaises(RuntimeError): - extract_document(source, errors="raise") - - def test_get_extension_basic(self): - cases = [ - ("file.pdf", "pdf"), - ("file.docx", "docx"), - ("file.odt", "odt"), - ("file.unknown", "unknown"), - ] - - for filename, expected in cases: - with self.subTest(filename=filename): - self.assertEqual(get_extension(filename), expected) - - def test_errors_configured(self): - self.assertEqual(set(ERRORS), {"ignore", "coerce", "raise"}) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/text/test_extractors.py b/test/text/test_extractors.py deleted file mode 100644 index 37c883d8..00000000 --- a/test/text/test_extractors.py +++ /dev/null @@ -1,164 +0,0 @@ -import tempfile -import unittest -from pathlib import Path -from unittest.mock import patch - -from aymurai.text.extractors.base import InvalidFile -from aymurai.text.extractors.docx import DocxExtractor -from aymurai.text.extractors.odt import OdtExtractor -from aymurai.text.extractors.pdf import PdfExtractor - - -class ExtractorTestCase(unittest.TestCase): - def setUp(self) -> None: - self.tempdir = tempfile.TemporaryDirectory() - self.addCleanup(self.tempdir.cleanup) - self.tmp_path = Path(self.tempdir.name) - - def test_docx_extractor_appends_footnotes(self): - file_path = self.tmp_path / "sample.docx" - file_path.write_bytes(b"fake docx content") - - with ( - patch( - "aymurai.text.extractors.docx.docx2txt.process", - return_value="Document body", - ), - patch( - "aymurai.text.extractors.docx.get_footnotes", - return_value=["Footnote one", ""], - ), - ): - extractor = DocxExtractor() - result = extractor.extract(file_path) - - self.assertIn("Document body", result) - self.assertTrue(result.strip().endswith("Footnote one")) - - def test_docx_extractor_raises_invalid_file(self): - file_path = self.tmp_path / "broken.docx" - file_path.write_bytes(b"") - - with patch( - "aymurai.text.extractors.docx.docx2txt.process", - side_effect=OSError("boom"), - ): - extractor = DocxExtractor() - with self.assertRaises(InvalidFile): - extractor.extract(file_path) - - def test_odt_extractor_prepends_header(self): - file_path = self.tmp_path / "sample.odt" - file_path.write_bytes(b"fake odt content") - - with ( - patch( - "aymurai.text.extractors.odt.odt_to_text", - return_value="Paragraph one", - ), - patch( - "aymurai.text.extractors.odt.get_header", - return_value=["Header"], - ), - ): - extractor = OdtExtractor() - result = extractor.extract(file_path) - - self.assertTrue(result.startswith("Header")) - self.assertIn("Paragraph one", result) - - def test_odt_extractor_invalid_file(self): - file_path = self.tmp_path / "broken.odt" - file_path.write_bytes(b"") - - with patch( - "aymurai.text.extractors.odt.odt_to_text", - side_effect=ValueError("bad xml"), - ): - extractor = OdtExtractor() - with self.assertRaises(InvalidFile): - extractor.extract(file_path) - - def test_pdf_extractor_delegates(self): - file_path = self.tmp_path / "sample.pdf" - file_path.write_bytes(b"fake pdf") - - with patch( - "aymurai.text.extractors.pdf.pdf_to_text", - return_value="PDF text", - ): - extractor = PdfExtractor() - result = extractor.extract(file_path) - - self.assertEqual(result, "PDF text") - - def test_pdf_extractor_passes_config(self): - file_path = self.tmp_path / "sample.pdf" - file_path.write_bytes(b"fake pdf") - - with patch( - "aymurai.text.extractors.pdf.pdf_to_text", - return_value="PDF text", - ) as pdf_to_text: - extractor = PdfExtractor() - extractor.extract( - file_path, - use_cache=False, - layout_batch_size=4, - detection_batch_size=5, - table_rec_batch_size=6, - recognition_batch_size=7, - ocr_error_batch_size=8, - force_ocr=False, - strip_existing_ocr=False, - torch_device="cpu", - debug=True, - ) - - pdf_to_text.assert_called_once_with( - file_path, - layout_batch_size=4, - detection_batch_size=5, - table_rec_batch_size=6, - recognition_batch_size=7, - ocr_error_batch_size=8, - force_ocr=False, - strip_existing_ocr=False, - torch_device="cpu", - debug=True, - ) - - def test_pdf_extractor_wraps_errors(self): - file_path = self.tmp_path / "broken.pdf" - file_path.write_bytes(b"") - - with patch( - "aymurai.text.extractors.pdf.pdf_to_text", - side_effect=ValueError("invalid"), - ): - extractor = PdfExtractor() - with self.assertRaises(InvalidFile): - extractor.extract(file_path) - - def test_odt_extractor_ignores_extra_kwargs(self): - file_path = self.tmp_path / "sample.odt" - file_path.write_bytes(b"fake odt content") - - with ( - patch( - "aymurai.text.extractors.odt.odt_to_text", - return_value="Paragraph one", - ), - patch( - "aymurai.text.extractors.odt.get_header", - return_value=[], - ), - ): - extractor = OdtExtractor() - result = extractor.extract(file_path, layout_batch_size=4) - - self.assertIn("Paragraph one", result) - - -if __name__ == "__main__": - unittest.main() From 0d3aae5ea1e0e5ab79b82d7257575fa7f087673a Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 20:14:02 +0000 Subject: [PATCH 084/101] =?UTF-8?q?=F0=9F=93=9D=20Update=20description=20f?= =?UTF-8?q?ormatting=20for=20aymurai=5Fdisambiguation=20field=20in=20Entit?= =?UTF-8?q?yAttributes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/meta/entities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aymurai/meta/entities.py b/aymurai/meta/entities.py index 56393e29..fe36b762 100644 --- a/aymurai/meta/entities.py +++ b/aymurai/meta/entities.py @@ -38,7 +38,7 @@ class EntityAttributes(BaseModel): ) aymurai_disambiguation: str | None = Field( None, - description=("Override disambiguation mode for this entity (none, fuzzy)."), + description="Override disambiguation mode for this entity (none, fuzzy).", ) aymurai_anonymize: bool | None = Field( None, From efecf50d954c97bc7ebe23d27467304ea76731cd Mon Sep 17 00:00:00 2001 From: jansaldo Date: Fri, 20 Feb 2026 20:23:57 +0000 Subject: [PATCH 085/101] =?UTF-8?q?=F0=9F=A6=96=20Update=20PdfExtractor.ex?= =?UTF-8?q?tract=20method=20to=20include=20ignored=20keyword=20arguments?= =?UTF-8?q?=20for=20backward=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/text/extractors/pdf.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/aymurai/text/extractors/pdf.py b/aymurai/text/extractors/pdf.py index 11708e2e..0e83c30d 100644 --- a/aymurai/text/extractors/pdf.py +++ b/aymurai/text/extractors/pdf.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Any from aymurai.text.extractors.base import BaseExtractor, InvalidFile, register_extractor from aymurai.text.extractors.utils import pdf_to_text @@ -8,11 +9,7 @@ class PdfExtractor(BaseExtractor): extension = "pdf" - def extract( - self, - path: Path, - y_tolerance: float | None = None, - ) -> str: + def extract(self, path: Path, y_tolerance: float | None = None, **_: Any) -> str: """ Extract normalized text from a PDF document. @@ -21,6 +18,7 @@ def extract( y_tolerance (float | None, optional): Maximum vertical gap used to merge nearby text blocks. If None, it is estimated from the document. Defaults to None. + **_ (Any): Ignored extra keyword arguments for backward compatibility. Returns: str: Cleaned textual content. From 837c639d47a2cc15c1cb0bfc57a640cf6f9d53dd Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 23 Feb 2026 18:35:01 +0000 Subject: [PATCH 086/101] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20static?= =?UTF-8?q?=20logo=20file=20from=20API=20resources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/api/Dockerfile | 2 -- resources/api/static/logo256-text.ico | Bin 1711 -> 0 bytes 2 files changed, 2 deletions(-) delete mode 100644 resources/api/static/logo256-text.ico diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index 660e5a7e..560e1290 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -73,7 +73,6 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ COPY --from=builder --chown=app:app /app/.venv /app/.venv # Copy static resources -COPY resources/api/static /resources/api/static COPY resources/pipelines /resources/pipelines # Set working directory @@ -94,7 +93,6 @@ ENV TF_CPP_MIN_LOG_LEVEL=3 \ RESOURCES_BASEPATH=resources # Copy additional resources -COPY ./resources/api resources/api COPY ./resources/pipelines/production resources/pipelines/production # Initialize application to download models diff --git a/resources/api/static/logo256-text.ico b/resources/api/static/logo256-text.ico deleted file mode 100644 index 9ff327f6d05389074da828615bd4afcf200f5945..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1711 zcmd^=|3A|S9LGPC2)TG%i*&b9W6hNu;j-dGm=9rLzLg14lXY3XjD_J4uBMN##bc{k zL|U>U^Chc0?s)hTy7Fz5d>OXYsfm0!{*CVae!O0f=i~8y{qXwj4FHIZ-5dm<58PA% zfL$A$MRX@GW)VP-#G9 zj2!pYx~o*Ge#zLh5@_52tsmbY^^LE3A-GBdDT?+zP-v`iOKd4(}{wTBa3Z<;nglNzoCA ze`tFl$CwC2XC0N=_S$m(;2F)JGP<_ZsC$K(Kn4~P=KSO`C*-3s z6d&8OAKNc9(hZ=1pMOo^=JP0KH+bBd=aszkA$4}Ni&#=TUae>{h?EpSf_b#q)}v&J zt2SpE&JWjtd7ozxrp`>a6wYzZi!}NT=h3;B+%DLuy;`ui>N?0iP-V4ecq;5l0N)Vj zWqrf?YXy=x1s`@h&bd72V3zZJ!KupKNZ6!o*!N^+RedJ7lsG*w`|SK|3s%`Lqw;JI zh!kg8j#$04ki^IJzn8r|hY##Vqhx*e_`a1GLyBH^;YJPkL7H#q*6lZyv2cF0;C1)m zVuyCYMcN6@tS}G^dKDwM?KW1bY}h3ms+0EJd#5N%=k=dReL2I%c$#A~_MXxk>dBFn zm++eHCH4^+9`RtBua!R|vWzMwS;iVzf=r8e1B;$SfyrZBFfJWuDy3m#=|)g(Nj~Zg zd&$iBKE2PvYBLCG$8@9qC5?1)J&Q}qN00sO&RTwAm35tpj^4eIAPfXCn+vL=!YT)( zS0l+nk}hHa^#rxQwQVSVFVf$&QPngTL#i$aShf*KMwVYyU z_r`r^I^6%h0d_NjI^Zh*aSobHi5M+$^<1LL!|HZq`{)Gw6Mw&{!cebC%;op<_pckp z1i76y!BCM@}Mf)+(pquwu{AAZQ#4$9Y(==xp?R0!WzfQhJVFkQ^ z%U6~q{TIGYoL3VLyR3D%5q5o;SnN)p{E$#_2){>EKe*&0P3Un{RR8G#hL#PnU6|4| zc8u(3_G^V9zNYO`%sKrAwXvdAr{bj8j#Rleo|~jpcqqoim3t*P?J1r(79N#1B>FTR z*+Ui?HBHZ5Ft-XJe7b|WGKaD$g!?kZi^ykH?>YJlzinTgm?0P5#T;B7D-NrWHS)?> z%!me9MH9&0cbzGXBZV1KdVw$KZ8K}`*774;;T4^A1KJ}lBVFB@e_802_{$XL>A_^& z5eg)tI1gnfHiPUF%auYfIL_g{O~Y!^XEX0?hbtWGpYXp DBCgg( From 846ae17ab2fe98ab2f21bcc05c81bc9af58619a8 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 23 Feb 2026 18:35:12 +0000 Subject: [PATCH 087/101] =?UTF-8?q?=F0=9F=94=A7=20Add=20version=5Fscheme?= =?UTF-8?q?=20configuration=20to=20setuptools=5Fscm=20in=20pyproject.toml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 1879831c..4e6e0475 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,6 +115,7 @@ aymurai-api = "aymurai.api.main:main" [tool.setuptools_scm] version_file = "aymurai/version.py" +version_scheme = "only-version" local_scheme = "no-local-version" From 63e96ea05ecb34287c1959d9cc2d2e39fdfa3b02 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 23 Feb 2026 18:35:50 +0000 Subject: [PATCH 088/101] =?UTF-8?q?=F0=9F=93=8C=20Update=20uv.lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uv.lock | 2350 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 1188 insertions(+), 1162 deletions(-) diff --git a/uv.lock b/uv.lock index 538334fc..738af2d2 100644 --- a/uv.lock +++ b/uv.lock @@ -1,12 +1,5 @@ version = 1 -revision = 3 requires-python = "==3.10.*" -resolution-markers = [ - "sys_platform == 'darwin'", - "python_version < '0'", - "platform_machine == 'aarch64' and sys_platform == 'linux'", - "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", -] [[package]] name = "accelerate" @@ -21,18 +14,18 @@ dependencies = [ { name = "safetensors" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/8e/ac2a9566747a93f8be36ee08532eb0160558b07630a081a6056a9f89bf1d/accelerate-1.12.0.tar.gz", hash = "sha256:70988c352feb481887077d2ab845125024b2a137a5090d6d7a32b57d03a45df6", size = 398399, upload-time = "2025-11-21T11:27:46.973Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/8e/ac2a9566747a93f8be36ee08532eb0160558b07630a081a6056a9f89bf1d/accelerate-1.12.0.tar.gz", hash = "sha256:70988c352feb481887077d2ab845125024b2a137a5090d6d7a32b57d03a45df6", size = 398399 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl", hash = "sha256:3e2091cd341423207e2f084a6654b1efcd250dc326f2a37d6dde446e07cabb11", size = 380935, upload-time = "2025-11-21T11:27:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl", hash = "sha256:3e2091cd341423207e2f084a6654b1efcd250dc326f2a37d6dde446e07cabb11", size = 380935 }, ] [[package]] name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, ] [[package]] @@ -49,25 +42,25 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, - { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, - { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, - { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, - { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, - { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, - { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, - { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, - { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, - { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, - { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, - { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, - { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, + { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950 }, + { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099 }, + { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072 }, + { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588 }, + { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334 }, + { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656 }, + { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625 }, + { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604 }, + { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370 }, + { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023 }, + { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680 }, + { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407 }, + { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047 }, + { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264 }, + { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275 }, + { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053 }, + { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687 }, ] [[package]] @@ -78,14 +71,14 @@ dependencies = [ { name = "frozenlist" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, ] [[package]] name = "alembic" -version = "1.17.2" +version = "1.18.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, @@ -93,27 +86,27 @@ dependencies = [ { name = "tomli" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064, upload-time = "2025-11-14T20:35:04.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554, upload-time = "2025-11-14T20:35:05.699Z" }, + { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893 }, ] [[package]] name = "annotated-doc" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] [[package]] @@ -125,18 +118,18 @@ dependencies = [ { name = "idna" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 }, ] [[package]] name = "appnope" version = "0.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, ] [[package]] @@ -146,9 +139,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, ] [[package]] @@ -158,23 +151,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, - { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549, upload-time = "2025-07-30T10:02:00.101Z" }, - { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539, upload-time = "2025-07-30T10:02:00.929Z" }, - { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467, upload-time = "2025-07-30T10:02:02.08Z" }, - { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355, upload-time = "2025-07-30T10:02:02.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187, upload-time = "2025-07-30T10:02:03.674Z" }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, + { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549 }, + { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539 }, + { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467 }, + { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355 }, + { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187 }, ] [[package]] @@ -185,52 +178,53 @@ dependencies = [ { name = "python-dateutil" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, ] [[package]] name = "asttokens" version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, ] [[package]] name = "async-lru" -version = "2.0.5" +version = "2.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380, upload-time = "2025-03-16T17:25:36.919Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8a/ca724066c32a53fa75f59e0f21aa822fdaa8a0dffa112d223634e3caabf9/async_lru-2.2.0.tar.gz", hash = "sha256:80abae2a237dbc6c60861d621619af39f0d920aea306de34cb992c879e01370c", size = 14654 } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069, upload-time = "2025-03-16T17:25:35.422Z" }, + { url = "https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl", hash = "sha256:e2c1cf731eba202b59c5feedaef14ffd9d02ad0037fcda64938699f2c380eafe", size = 7890 }, ] [[package]] name = "async-timeout" version = "5.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, ] [[package]] name = "attrs" version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, ] [[package]] name = "aymurai" +version = "1.5.0rc1" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -333,11 +327,11 @@ dev = [ [[package]] name = "babel" -version = "2.17.0" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 }, ] [[package]] @@ -348,9 +342,9 @@ dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, ] [[package]] @@ -364,9 +358,9 @@ dependencies = [ { name = "lxml" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924, upload-time = "2023-08-15T22:34:59.047Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419, upload-time = "2023-08-15T22:34:56.022Z" }, + { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419 }, ] [[package]] @@ -376,9 +370,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533, upload-time = "2025-10-27T17:57:39.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437, upload-time = "2025-10-27T17:57:37.538Z" }, + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, ] [package.optional-dependencies] @@ -388,48 +382,48 @@ css = [ [[package]] name = "boto3" -version = "1.42.23" +version = "1.42.54" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/df/17828670134e56ffca8cf8b017477f16d1a9df7ecfc3870d02aa6d4d2e20/boto3-1.42.23.tar.gz", hash = "sha256:f681a8d43b46b3d8acf0be4f3894eb85e40e75945431d0dfe0542edda7025512", size = 112845, upload-time = "2026-01-06T20:28:51.006Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/53/2e0a325e080bd83f5dfd8f964b70b93badc284bcb5680bee75327771ad4a/boto3-1.42.54.tar.gz", hash = "sha256:fe3d8ec586c39a0c96327fd317c77ca601ec5f991e9ba7211cacae8db4c07a73", size = 112747 } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/8b/8e028dbf6c4bf36defe899882a00c11a8d22fcb7348e88058900c13b72a0/boto3-1.42.23-py3-none-any.whl", hash = "sha256:2ed797bdb394b08550f6269babf0a31bbeb853684bb2cb67116620df0ed632dc", size = 140574, upload-time = "2026-01-06T20:28:48.992Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d6/695283df0a613cb723a05745cd565061add2bc5655d3493341b8b5c6b81d/boto3-1.42.54-py3-none-any.whl", hash = "sha256:71194e855bfc81a21872cbe29c41f52ffdbe67e0a184a52c13346ef00b328939", size = 140555 }, ] [[package]] name = "botocore" -version = "1.42.23" +version = "1.42.54" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/2c/db33716f86b67c514f895c60694a25cd7428d2137b574b59d09d626b0e2e/botocore-1.42.23.tar.gz", hash = "sha256:453ce449bd1021acd67e75c814aae1b132b1ab3ee0ecff248de863bf19e58be8", size = 14878387, upload-time = "2026-01-06T20:28:40.151Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/9a/5ab14330e5d1c3489e91f32f6ece40f3b58cf82d2aafe1e4a61711f616b0/botocore-1.42.54.tar.gz", hash = "sha256:ab203d4e57d22913c8386a695d048e003b7508a8a4a7a46c9ddf4ebd67a20b69", size = 14921929 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/e6/114d66f81fc4b77a8c1e45e6fcf0021018d1f4eefca0905b3da8023c9a47/botocore-1.42.23-py3-none-any.whl", hash = "sha256:d5042e0252b81f25ca1152fff9ed25463bab2438fbc4530ba53d5390d00ca1b1", size = 14551561, upload-time = "2026-01-06T20:28:36.418Z" }, + { url = "https://files.pythonhosted.org/packages/86/29/cdf4ba5d0f626b7c5a74d6a615b977469960eae8c67f8e4213941f5f3dfd/botocore-1.42.54-py3-none-any.whl", hash = "sha256:853a0822de66d060aeebafa07ca13a03799f7958313d1b29f8dc7e2e1be8f527", size = 14594249 }, ] [[package]] name = "cachetools" -version = "6.2.4" +version = "7.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731, upload-time = "2025-12-15T18:24:53.744Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/07/56595285564e90777d758ebd383d6b0b971b87729bbe2184a849932a3736/cachetools-7.0.1.tar.gz", hash = "sha256:e31e579d2c5b6e2944177a0397150d312888ddf4e16e12f1016068f0c03b8341", size = 36126 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551, upload-time = "2025-12-15T18:24:52.332Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9e/5faefbf9db1db466d633735faceda1f94aa99ce506ac450d232536266b32/cachetools-7.0.1-py3-none-any.whl", hash = "sha256:8f086515c254d5664ae2146d14fc7f65c9a4bce75152eb247e5a9c5e6d7b2ecf", size = 13484 }, ] [[package]] name = "certifi" version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900 }, ] [[package]] @@ -439,54 +433,54 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283 }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504 }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811 }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402 }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217 }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079 }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475 }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829 }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211 }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184 }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790 }, ] [[package]] name = "cfgv" version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, ] [[package]] name = "charset-normalizer" version = "3.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, - { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, - { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, - { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, - { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, - { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, - { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, - { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, - { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, - { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, - { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, ] [[package]] @@ -494,38 +488,38 @@ name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "platform_system == 'Windows'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] name = "comm" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, ] [[package]] name = "conllu" version = "4.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768, upload-time = "2023-06-19T12:37:49.632Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098, upload-time = "2023-06-19T12:37:47.885Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098 }, ] [[package]] @@ -535,35 +529,55 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, - { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, - { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, - { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, - { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, - { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, - { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, - { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, - { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, - { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, - { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, - { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551 }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399 }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061 }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956 }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872 }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027 }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641 }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075 }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534 }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188 }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681 }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101 }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599 }, +] + +[[package]] +name = "cuda-bindings" +version = "12.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/31/bfcc870f69c6a017c4ad5c42316207fc7551940db6f3639aa4466ec5faf3/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a022c96b8bd847e8dc0675523431149a4c3e872f440e3002213dbb9e08f0331a", size = 11800959 }, + { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218 }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/5e/db279a3bfbd18d59d0598922a3b3c1454908d0969e8372260afec9736376/cuda_pathfinder-1.3.4-py3-none-any.whl", hash = "sha256:fb983f6e0d43af27ef486e14d5989b5f904ef45cedf40538bfdcbffa6bb01fb2", size = 30878 }, ] [[package]] name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, ] [[package]] name = "datasets" -version = "4.4.2" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -581,9 +595,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/54/9359803da96bc65439a28fbb014dc2c90b7d4d8034a93b72362b0d40191f/datasets-4.4.2.tar.gz", hash = "sha256:9de16e415c4ba4713eac0493f7c7dc74f3aa21599297f00cc6ddab409cb7b24b", size = 586474, upload-time = "2025-12-19T15:03:09.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/bf/bb927bde63d649296c83e883171ae77074717c1b80fe2868b328bd0dbcbb/datasets-4.5.0.tar.gz", hash = "sha256:00c698ce1c2452e646cc5fad47fef39d3fe78dd650a8a6eb205bb45eb63cd500", size = 588384 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/b5/fefa518c809de7bced5cddb7c21c010da66fa2ae494bda96844a280cc6ce/datasets-4.4.2-py3-none-any.whl", hash = "sha256:6f5ef3417504d9cd663c71c1b90b9a494ff4c2076a2cd6a6e40ceee6ad95befc", size = 512268, upload-time = "2025-12-19T15:03:07.087Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d5/0d563ea3c205eee226dc8053cf7682a8ac588db8acecd0eda2b587987a0b/datasets-4.5.0-py3-none-any.whl", hash = "sha256:b5d7e08096ffa407dd69e58b1c0271c9b2506140839b8d99af07375ad31b6726", size = 515196 }, ] [[package]] @@ -593,33 +607,33 @@ source = { git = "https://github.com/jedzill4/datetime_matcher#0e5793e8d1e3653f7 [[package]] name = "debugpy" -version = "1.8.19" +version = "1.8.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/75/9e12d4d42349b817cd545b89247696c67917aab907012ae5b64bbfea3199/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb", size = 1644590, upload-time = "2025-12-15T21:53:28.044Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/98/d57054371887f37d3c959a7a8dc3c76b763acb65f5e78d849d7db7cadc5b/debugpy-1.8.19-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fce6da15d73be5935b4438435c53adb512326a3e11e4f90793ea87cd9f018254", size = 2098493, upload-time = "2025-12-15T21:53:30.149Z" }, - { url = "https://files.pythonhosted.org/packages/ee/dd/c517b9aa3500157a30e4f4c4f5149f880026bd039d2b940acd2383a85d8e/debugpy-1.8.19-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:e24b1652a1df1ab04d81e7ead446a91c226de704ff5dde6bd0a0dbaab07aa3f2", size = 3087875, upload-time = "2025-12-15T21:53:31.511Z" }, - { url = "https://files.pythonhosted.org/packages/d8/57/3d5a5b0da9b63445253107ead151eff29190c6ad7440c68d1a59d56613aa/debugpy-1.8.19-cp310-cp310-win32.whl", hash = "sha256:327cb28c3ad9e17bc925efc7f7018195fd4787c2fe4b7af1eec11f1d19bdec62", size = 5239378, upload-time = "2025-12-15T21:53:32.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/36/7f9053c4c549160c87ae7e43800138f2695578c8b65947114c97250983b6/debugpy-1.8.19-cp310-cp310-win_amd64.whl", hash = "sha256:b7dd275cf2c99e53adb9654f5ae015f70415bbe2bacbe24cfee30d54b6aa03c5", size = 5271129, upload-time = "2025-12-15T21:53:35.085Z" }, - { url = "https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38", size = 5292321, upload-time = "2025-12-15T21:54:16.024Z" }, + { url = "https://files.pythonhosted.org/packages/71/be/8bd693a0b9d53d48c8978fa5d889e06f3b5b03e45fd1ea1e78267b4887cb/debugpy-1.8.20-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:157e96ffb7f80b3ad36d808646198c90acb46fdcfd8bb1999838f0b6f2b59c64", size = 2099192 }, + { url = "https://files.pythonhosted.org/packages/77/1b/85326d07432086a06361d493d2743edd0c4fc2ef62162be7f8618441ac37/debugpy-1.8.20-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:c1178ae571aff42e61801a38b007af504ec8e05fde1c5c12e5a7efef21009642", size = 3088568 }, + { url = "https://files.pythonhosted.org/packages/e8/60/3e08462ee3eccd10998853eb35947c416e446bfe2bc37dbb886b9044586c/debugpy-1.8.20-cp310-cp310-win32.whl", hash = "sha256:c29dd9d656c0fbd77906a6e6a82ae4881514aa3294b94c903ff99303e789b4a2", size = 5284399 }, + { url = "https://files.pythonhosted.org/packages/72/43/09d49106e770fe558ced5e80df2e3c2ebee10e576eda155dcc5670473663/debugpy-1.8.20-cp310-cp310-win_amd64.whl", hash = "sha256:3ca85463f63b5dd0aa7aaa933d97cbc47c174896dcae8431695872969f981893", size = 5316388 }, + { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658 }, ] [[package]] name = "decorator" version = "5.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, ] [[package]] name = "defusedxml" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, ] [[package]] @@ -629,60 +643,60 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, ] [[package]] name = "dill" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668 }, ] [[package]] name = "diskcache" version = "5.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, ] [[package]] name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, ] [[package]] name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } [[package]] name = "docx2txt" version = "0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613, upload-time = "2025-03-24T20:59:25.21Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025, upload-time = "2025-03-24T20:59:24.394Z" }, + { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025 }, ] [[package]] @@ -693,9 +707,9 @@ dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, ] [[package]] @@ -705,18 +719,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 }, ] [[package]] name = "executing" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, ] [[package]] @@ -726,24 +740,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644, upload-time = "2023-06-27T15:24:28.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644 } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039, upload-time = "2023-06-27T15:24:24.869Z" }, + { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039 }, ] [[package]] name = "fastapi" -version = "0.128.0" +version = "0.132.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/55/f1b4d4e478a0a1b4b1113d0f610a1b08e539b69900f97fdc97155d62fdee/fastapi-0.132.0.tar.gz", hash = "sha256:ef687847936d8a57ea6ea04cf9a85fe5f2c6ba64e22bfa721467094b69d48d92", size = 372422 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, + { url = "https://files.pythonhosted.org/packages/a8/de/6171c3363bbc5e01686e200e0880647c9270daa476d91030435cf14d32f5/fastapi-0.132.0-py3-none-any.whl", hash = "sha256:3c487d5afce196fa8ea509ae1531e96ccd5cdd2fd6eae78b73e2c20fba706689", size = 104652 }, ] [package.optional-dependencies] @@ -760,7 +775,7 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.20" +version = "0.0.23" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, @@ -768,9 +783,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/ca/d90fb3bfbcbd6e56c77afd9d114dd6ce8955d8bb90094399d1c70e659e40/fastapi_cli-0.0.20.tar.gz", hash = "sha256:d17c2634f7b96b6b560bc16b0035ed047d523c912011395f49f00a421692bc3a", size = 19786, upload-time = "2025-12-22T17:13:33.794Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/9f/cbd463e57de4e977b8ea0403f95347f9150441568b1d3fe3e4949ef80ef3/fastapi_cli-0.0.23.tar.gz", hash = "sha256:210ac280ea41e73aac5a57688781256beb23c2cba3a41266896fa43e6445c8e7", size = 19763 } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/89/5c4eef60524d0fd704eb0706885b82cd5623a43396b94e4a5b17d3a3f516/fastapi_cli-0.0.20-py3-none-any.whl", hash = "sha256:e58b6a0038c0b1532b7a0af690656093dee666201b6b19d3c87175b358e9f783", size = 12390, upload-time = "2025-12-22T17:13:31.708Z" }, + { url = "https://files.pythonhosted.org/packages/68/89/19dcfd5cd289b306abdcabac68b88a4f54b7710a2c33adc16a337ecdcdfa/fastapi_cli-0.0.23-py3-none-any.whl", hash = "sha256:7e9634fc212da0b6cfc75bd3ac366cc9dfdb43b5e9ec12e58bfd1acdd2697f25", size = 12305 }, ] [package.optional-dependencies] @@ -781,7 +796,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.8.0" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastar" }, @@ -793,61 +808,61 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/5d/3b33438de35521fab4968b232caa9a4bd568a5078f2b2dfb7bb8a4528603/fastapi_cloud_cli-0.8.0.tar.gz", hash = "sha256:cf07c502528bfd9e6b184776659f05d9212811d76bbec9fbb6bf34bed4c7456f", size = 30257, upload-time = "2025-12-23T12:08:33.904Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/0b/f07f4976784978ef159fd2e8f5c16f1f9d610578fb1fd976ff1315c11ea6/fastapi_cloud_cli-0.13.0.tar.gz", hash = "sha256:4d8f42337e8021c648f6cb0672de7d5b31b0fc7387a83d7b12f974600ac3f2fd", size = 38436 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/8e/abb95ef59e91bb5adaa2d18fbf9ea70fd524010bb03f406a2dd2a4775ef9/fastapi_cloud_cli-0.8.0-py3-none-any.whl", hash = "sha256:e9f40bee671d985fd25d7a5409b56d4f103777bf8a0c6d746ea5fbf97a8186d9", size = 22306, upload-time = "2025-12-23T12:08:32.68Z" }, + { url = "https://files.pythonhosted.org/packages/b4/88/71a1e989d17b9edb483f32e28b7891ffdd3005271518c98ba6415987c430/fastapi_cloud_cli-0.13.0-py3-none-any.whl", hash = "sha256:874a9ed8dba34ec828f198c72de9f9a38de77ac1b15083d6bc3a4d772b0bc477", size = 27631 }, ] [[package]] name = "fastar" version = "0.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536, upload-time = "2025-11-26T02:34:35.236Z" }, - { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235, upload-time = "2025-11-26T02:34:19.367Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386, upload-time = "2025-11-26T02:33:47.613Z" }, - { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955, upload-time = "2025-11-26T02:32:44.279Z" }, - { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987, upload-time = "2025-11-26T02:32:59.701Z" }, - { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900, upload-time = "2025-11-26T02:33:16.059Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523, upload-time = "2025-11-26T02:33:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268, upload-time = "2025-11-26T02:34:04.003Z" }, - { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286, upload-time = "2025-11-26T02:34:50.279Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921, upload-time = "2025-11-26T02:35:07.292Z" }, - { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302, upload-time = "2025-11-26T02:35:24.995Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141, upload-time = "2025-11-26T02:35:42.449Z" }, - { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544, upload-time = "2025-11-26T02:36:23.801Z" }, - { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701, upload-time = "2025-11-26T02:36:09.625Z" }, - { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544, upload-time = "2025-11-26T02:34:46.195Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020, upload-time = "2025-11-26T02:34:31.085Z" }, - { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735, upload-time = "2025-11-26T02:34:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779, upload-time = "2025-11-26T02:32:55.109Z" }, - { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755, upload-time = "2025-11-26T02:33:11.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732, upload-time = "2025-11-26T02:33:27.122Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571, upload-time = "2025-11-26T02:33:42.986Z" }, - { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440, upload-time = "2025-11-26T02:34:15.439Z" }, - { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424, upload-time = "2025-11-26T02:35:02.742Z" }, - { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675, upload-time = "2025-11-26T02:35:20.252Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098, upload-time = "2025-11-26T02:35:37.643Z" }, - { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397, upload-time = "2025-11-26T02:35:56.215Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536 }, + { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235 }, + { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386 }, + { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955 }, + { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987 }, + { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900 }, + { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523 }, + { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268 }, + { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286 }, + { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921 }, + { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302 }, + { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141 }, + { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544 }, + { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701 }, + { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544 }, + { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020 }, + { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735 }, + { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779 }, + { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755 }, + { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732 }, + { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571 }, + { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440 }, + { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424 }, + { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675 }, + { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098 }, + { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397 }, ] [[package]] name = "fastjsonschema" version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, ] [[package]] name = "filelock" -version = "3.20.2" +version = "3.24.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510, upload-time = "2026-01-02T15:33:32.582Z" } +sdist = { url = "https://files.pythonhosted.org/packages/73/92/a8e2479937ff39185d20dd6a851c1a63e55849e447a55e798cc2e1f49c65/filelock-3.24.3.tar.gz", hash = "sha256:011a5644dc937c22699943ebbfc46e969cdde3e171470a6e40b9533e5a72affa", size = 37935 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/9c/0f/5d0c71a1aefeb08efff26272149e07ab922b64f46c63363756224bd6872e/filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d", size = 24331 }, ] [[package]] @@ -857,9 +872,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "termcolor" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720, upload-time = "2025-08-16T20:20:24.175Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945, upload-time = "2025-08-16T20:20:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945 }, ] [[package]] @@ -893,69 +908,69 @@ dependencies = [ { name = "transformers", extra = ["sentencepiece"] }, { name = "wikipedia-api" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607, upload-time = "2025-02-05T14:45:44.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604, upload-time = "2025-02-05T14:45:41.788Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, ] [[package]] name = "fonttools" version = "4.61.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799, upload-time = "2025-12-12T17:29:27.5Z" }, - { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032, upload-time = "2025-12-12T17:29:30.115Z" }, - { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863, upload-time = "2025-12-12T17:29:32.535Z" }, - { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076, upload-time = "2025-12-12T17:29:34.907Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623, upload-time = "2025-12-12T17:29:37.33Z" }, - { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327, upload-time = "2025-12-12T17:29:39.781Z" }, - { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180, upload-time = "2025-12-12T17:29:42.217Z" }, - { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654, upload-time = "2025-12-12T17:29:44.564Z" }, - { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" }, + { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799 }, + { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032 }, + { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863 }, + { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076 }, + { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623 }, + { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327 }, + { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180 }, + { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654 }, + { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996 }, ] [[package]] name = "fqdn" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, ] [[package]] name = "frozenlist" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, - { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, - { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, - { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, - { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, - { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, - { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, - { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, - { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, - { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, - { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, - { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230 }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621 }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889 }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464 }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649 }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188 }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748 }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351 }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767 }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887 }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785 }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312 }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650 }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659 }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837 }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, ] [[package]] name = "fsspec" version = "2025.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966 }, ] [package.optional-dependencies] @@ -970,14 +985,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, ] [[package]] name = "gdown" -version = "5.2.0" +version = "5.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, @@ -985,49 +1000,49 @@ dependencies = [ { name = "requests", extra = ["socks"] }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/6a/37e6b70c5bda3161e40265861e63b64a86bfc6ca6a8f1c35328a675c84fd/gdown-5.2.0.tar.gz", hash = "sha256:2145165062d85520a3cd98b356c9ed522c5e7984d408535409fd46f94defc787", size = 284647, upload-time = "2024-05-12T06:45:12.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/cf/919a9fa16faf8e4572a24d941353edaf4d54e3ddcd048e6c1aeb8c7a9903/gdown-5.2.1.tar.gz", hash = "sha256:247c2ad1f579db5b66b54c04e6a871995fc8fd7021708b950b8ba7b32cf90323", size = 284743 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/70/e07c381e6488a77094f04c85c9caf1c8008cdc30778f7019bc52e5285ef0/gdown-5.2.0-py3-none-any.whl", hash = "sha256:33083832d82b1101bdd0e9df3edd0fbc0e1c5f14c9d8c38d2a35bf1683b526d6", size = 18235, upload-time = "2024-05-12T06:45:10.017Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/35dd0a0b7428bd67b12b358d7b4277f693493a3839b071d540a4c8357b78/gdown-5.2.1-py3-none-any.whl", hash = "sha256:391f0480d495fb87644d1a1ee3ddfeb2144e1de31408fbc74f7e3b3ba927052b", size = 18241 }, ] [[package]] name = "greenlet" -version = "3.3.0" +version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267 } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658, upload-time = "2025-12-04T14:23:37.494Z" }, - { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810, upload-time = "2025-12-04T14:50:04.154Z" }, - { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248, upload-time = "2025-12-04T14:57:39.35Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403, upload-time = "2025-12-04T15:07:10.831Z" }, - { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910, upload-time = "2025-12-04T14:25:59.705Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206, upload-time = "2025-12-04T15:04:21.027Z" }, - { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359, upload-time = "2025-12-04T14:27:26.548Z" }, - { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740, upload-time = "2025-12-04T14:47:52.773Z" }, + { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747 }, + { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202 }, + { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620 }, + { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132 }, + { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729 }, + { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946 }, + { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494 }, + { url = "https://files.pythonhosted.org/packages/ac/78/f93e840cbaef8becaf6adafbaf1319682a6c2d8c1c20224267a5c6c8c891/greenlet-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f", size = 230092 }, ] [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] [[package]] name = "hf-xet" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020 } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099 }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178 }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214 }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054 }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812 }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920 }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 }, ] [[package]] @@ -1038,24 +1053,24 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, ] [[package]] name = "httptools" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" }, - { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" }, - { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" }, - { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" }, - { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531 }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408 }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889 }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460 }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267 }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429 }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173 }, ] [[package]] @@ -1068,14 +1083,14 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] [[package]] name = "huggingface-hub" -version = "0.36.0" +version = "0.36.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -1087,27 +1102,27 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358, upload-time = "2025-10-23T12:12:01.413Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094, upload-time = "2025-10-23T12:11:59.557Z" }, + { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395 }, ] [[package]] name = "identify" -version = "2.6.15" +version = "2.6.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, + { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202 }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, ] [[package]] @@ -1117,17 +1132,17 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/53/c3/b2afa612aa0373f3e6bb190e6de35f293b307d1537f109e3e25dbfcdf212/intervaltree-3.2.1.tar.gz", hash = "sha256:f3f7e8baeb7dd75b9f7a6d33cf3ec10025984a8e66e3016d537e52130c73cfe2", size = 1231531, upload-time = "2025-12-24T04:25:06.773Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/c3/b2afa612aa0373f3e6bb190e6de35f293b307d1537f109e3e25dbfcdf212/intervaltree-3.2.1.tar.gz", hash = "sha256:f3f7e8baeb7dd75b9f7a6d33cf3ec10025984a8e66e3016d537e52130c73cfe2", size = 1231531 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/7f/8a80a1c7c2ed05822b5a2b312d2995f30c533641f8198366ba2e26a7bb03/intervaltree-3.2.1-py2.py3-none-any.whl", hash = "sha256:a8a8381bbd35d48ceebee932c77ffc988492d22fb1d27d0ba1d74a7694eb8f0b", size = 25929, upload-time = "2025-12-24T04:25:05.298Z" }, + { url = "https://files.pythonhosted.org/packages/83/7f/8a80a1c7c2ed05822b5a2b312d2995f30c533641f8198366ba2e26a7bb03/intervaltree-3.2.1-py2.py3-none-any.whl", hash = "sha256:a8a8381bbd35d48ceebee932c77ffc988492d22fb1d27d0ba1d74a7694eb8f0b", size = 25929 }, ] [[package]] name = "ipykernel" -version = "7.1.0" +version = "7.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "appnope", marker = "platform_system == 'Darwin'" }, { name = "comm" }, { name = "debugpy" }, { name = "ipython" }, @@ -1141,9 +1156,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579, upload-time = "2025-10-27T09:46:39.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968, upload-time = "2025-10-27T09:46:37.805Z" }, + { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788 }, ] [[package]] @@ -1163,9 +1178,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813 }, ] [[package]] @@ -1179,9 +1194,9 @@ dependencies = [ { name = "traitlets" }, { name = "widgetsnbextension" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, ] [[package]] @@ -1191,9 +1206,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "arrow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, ] [[package]] @@ -1203,9 +1218,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parso" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, ] [[package]] @@ -1215,9 +1230,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] @@ -1228,36 +1243,36 @@ dependencies = [ { name = "click" }, { name = "rapidfuzz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537, upload-time = "2024-11-01T16:18:57.337Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990, upload-time = "2024-11-01T16:18:55.928Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990 }, ] [[package]] name = "jmespath" -version = "1.0.1" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419 }, ] [[package]] name = "joblib" version = "1.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, ] [[package]] name = "json5" version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441, upload-time = "2026-01-01T19:42:14.99Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163, upload-time = "2026-01-01T19:42:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163 }, ] [[package]] @@ -1267,18 +1282,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359, upload-time = "2023-09-01T12:34:44.187Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701, upload-time = "2023-09-01T12:34:42.563Z" }, + { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701 }, ] [[package]] name = "jsonpointer" version = "3.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, ] [[package]] @@ -1291,9 +1306,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, ] [package.optional-dependencies] @@ -1316,9 +1331,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, ] [[package]] @@ -1333,14 +1348,14 @@ dependencies = [ { name = "nbconvert" }, { name = "notebook" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, ] [[package]] name = "jupyter-client" -version = "8.7.0" +version = "8.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-core" }, @@ -1349,9 +1364,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/27/d10de45e8ad4ce872372c4a3a37b7b35b6b064f6f023a5c14ffcced4d59d/jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d", size = 344691, upload-time = "2025-12-09T18:37:01.953Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0", size = 106215, upload-time = "2025-12-09T18:37:00.024Z" }, + { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371 }, ] [[package]] @@ -1368,9 +1383,9 @@ dependencies = [ { name = "pyzmq" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, ] [[package]] @@ -1381,9 +1396,9 @@ dependencies = [ { name = "platformdirs" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, ] [[package]] @@ -1400,9 +1415,9 @@ dependencies = [ { name = "rfc3986-validator" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, + { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430 }, ] [[package]] @@ -1412,9 +1427,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823, upload-time = "2025-08-27T17:47:34.671Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687, upload-time = "2025-08-27T17:47:33.15Z" }, + { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687 }, ] [[package]] @@ -1434,7 +1449,7 @@ dependencies = [ { name = "overrides" }, { name = "packaging" }, { name = "prometheus-client" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "pyzmq" }, { name = "send2trash" }, { name = "terminado" }, @@ -1442,27 +1457,27 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949, upload-time = "2025-08-21T14:42:54.042Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221, upload-time = "2025-08-21T14:42:52.034Z" }, + { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221 }, ] [[package]] name = "jupyter-server-terminals" -version = "0.5.3" +version = "0.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "terminado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430, upload-time = "2024-03-12T14:37:03.049Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656, upload-time = "2024-03-12T14:37:00.708Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704 }, ] [[package]] name = "jupyterlab" -version = "4.5.1" +version = "4.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1480,18 +1495,18 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/21/413d142686a4e8f4268d985becbdb4daf060524726248e73be4773786987/jupyterlab-4.5.1.tar.gz", hash = "sha256:09da1ddfbd9eec18b5101dbb8515612aa1e47443321fb99503725a88e93d20d9", size = 23992251, upload-time = "2025-12-15T16:58:59.361Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/6b/21af7c0512bdf67e0c54c121779a1f2a97a164a7657e13fced79db8fa5a0/jupyterlab-4.5.4.tar.gz", hash = "sha256:c215f48d8e4582bd2920ad61cc6a40d8ebfef7e5a517ae56b8a9413c9789fdfb", size = 23943597 } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-py3-none-any.whl", hash = "sha256:31b059de96de0754ff1f2ce6279774b6aab8c34d7082e9752db58207c99bd514", size = 12384821, upload-time = "2025-12-15T16:58:55.563Z" }, + { url = "https://files.pythonhosted.org/packages/f5/9f/a70972ece62ead2d81acc6223188f6d18a92f665ccce17796a0cdea4fcf5/jupyterlab-4.5.4-py3-none-any.whl", hash = "sha256:cc233f70539728534669fb0015331f2a3a87656207b3bb2d07916e9289192f12", size = 12391867 }, ] [[package]] name = "jupyterlab-pygments" version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, ] [[package]] @@ -1507,44 +1522,44 @@ dependencies = [ { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, ] [[package]] name = "jupyterlab-widgets" version = "3.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, ] [[package]] name = "kiwisolver" version = "1.4.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" }, - { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" }, - { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" }, - { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" }, - { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" }, - { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" }, - { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" }, - { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" }, - { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" }, - { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" }, - { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" }, - { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" }, - { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" }, - { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" }, - { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159 }, + { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578 }, + { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312 }, + { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458 }, + { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640 }, + { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074 }, + { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036 }, + { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310 }, + { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943 }, + { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488 }, + { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787 }, + { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730 }, + { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036 }, + { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183 }, + { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675 }, + { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277 }, + { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994 }, + { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744 }, ] [[package]] @@ -1554,15 +1569,15 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474, upload-time = "2021-05-07T07:54:13.562Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474 } [[package]] name = "lark" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, ] [[package]] @@ -1572,39 +1587,39 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fire" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292, upload-time = "2022-09-06T16:09:06.607Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594, upload-time = "2022-09-06T16:09:04.658Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594 }, ] [[package]] name = "lxml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589, upload-time = "2025-09-22T04:00:10.51Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671, upload-time = "2025-09-22T04:00:15.411Z" }, - { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961, upload-time = "2025-09-22T04:00:17.619Z" }, - { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087, upload-time = "2025-09-22T04:00:19.868Z" }, - { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620, upload-time = "2025-09-22T04:00:21.877Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664, upload-time = "2025-09-22T04:00:23.714Z" }, - { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397, upload-time = "2025-09-22T04:00:25.544Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178, upload-time = "2025-09-22T04:00:27.602Z" }, - { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148, upload-time = "2025-09-22T04:00:29.323Z" }, - { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035, upload-time = "2025-09-22T04:00:31.061Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111, upload-time = "2025-09-22T04:00:33.11Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662, upload-time = "2025-09-22T04:00:35.237Z" }, - { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973, upload-time = "2025-09-22T04:00:37.086Z" }, - { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953, upload-time = "2025-09-22T04:00:39.224Z" }, - { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695, upload-time = "2025-09-22T04:00:41.402Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051, upload-time = "2025-09-22T04:00:43.525Z" }, - { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264, upload-time = "2025-09-22T04:04:32.892Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435, upload-time = "2025-09-22T04:04:34.907Z" }, - { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913, upload-time = "2025-09-22T04:04:37.205Z" }, - { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357, upload-time = "2025-09-22T04:04:39.322Z" }, - { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295, upload-time = "2025-09-22T04:04:41.647Z" }, - { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913, upload-time = "2025-09-22T04:04:43.602Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589 }, + { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671 }, + { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961 }, + { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087 }, + { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620 }, + { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664 }, + { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397 }, + { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178 }, + { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148 }, + { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035 }, + { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111 }, + { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662 }, + { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973 }, + { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953 }, + { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695 }, + { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051 }, + { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264 }, + { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435 }, + { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913 }, + { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357 }, + { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295 }, + { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913 }, ] [[package]] @@ -1614,9 +1629,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, ] [[package]] @@ -1626,28 +1641,28 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, - { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, - { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, - { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, - { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631 }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057 }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050 }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681 }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705 }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524 }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282 }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745 }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571 }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056 }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932 }, ] [[package]] @@ -1665,17 +1680,17 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, - { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, - { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, - { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, + { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828 }, + { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050 }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452 }, + { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928 }, + { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377 }, + { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127 }, + { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252 }, + { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693 }, + { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205 }, ] [[package]] @@ -1685,18 +1700,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110 } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516 }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] [[package]] @@ -1706,18 +1721,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467, upload-time = "2025-12-23T11:36:34.994Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598 }, ] [[package]] name = "more-itertools" version = "10.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667 }, ] [[package]] @@ -1728,48 +1743,48 @@ dependencies = [ { name = "jinja2" }, { name = "matplotlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433, upload-time = "2025-11-05T18:12:24.183Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051, upload-time = "2025-11-05T18:12:22.527Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, ] [[package]] name = "multidict" -version = "6.7.0" +version = "6.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153, upload-time = "2025-10-06T14:48:26.409Z" }, - { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993, upload-time = "2025-10-06T14:48:28.4Z" }, - { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607, upload-time = "2025-10-06T14:48:29.581Z" }, - { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847, upload-time = "2025-10-06T14:48:32.107Z" }, - { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616, upload-time = "2025-10-06T14:48:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333, upload-time = "2025-10-06T14:48:35.9Z" }, - { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239, upload-time = "2025-10-06T14:48:37.302Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618, upload-time = "2025-10-06T14:48:38.963Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655, upload-time = "2025-10-06T14:48:40.312Z" }, - { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245, upload-time = "2025-10-06T14:48:41.848Z" }, - { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523, upload-time = "2025-10-06T14:48:43.749Z" }, - { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129, upload-time = "2025-10-06T14:48:45.225Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999, upload-time = "2025-10-06T14:48:46.703Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711, upload-time = "2025-10-06T14:48:48.146Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504, upload-time = "2025-10-06T14:48:49.447Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422, upload-time = "2025-10-06T14:48:50.789Z" }, - { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050, upload-time = "2025-10-06T14:48:51.938Z" }, - { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153, upload-time = "2025-10-06T14:48:53.146Z" }, - { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176 }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996 }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631 }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561 }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223 }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322 }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005 }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173 }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273 }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956 }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477 }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615 }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930 }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807 }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103 }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416 }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022 }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238 }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319 }, ] [[package]] @@ -1779,23 +1794,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503, upload-time = "2025-04-17T03:11:27.742Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083, upload-time = "2025-04-17T03:11:04.223Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128, upload-time = "2025-04-17T03:11:06.045Z" }, - { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132, upload-time = "2025-04-17T03:11:07.533Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948, upload-time = "2025-04-17T03:11:20.223Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636, upload-time = "2025-04-17T03:11:24.936Z" }, - { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083 }, + { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128 }, + { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132 }, + { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948 }, + { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636 }, + { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478 }, ] [[package]] name = "narwhals" -version = "2.15.0" +version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/6d/b57c64e5038a8cf071bce391bb11551657a74558877ac961e7fa905ece27/narwhals-2.15.0.tar.gz", hash = "sha256:a9585975b99d95084268445a1fdd881311fa26ef1caa18020d959d5b2ff9a965", size = 603479, upload-time = "2026-01-06T08:10:13.27Z" } +sdist = { url = "https://files.pythonhosted.org/packages/75/59/81d0f4cad21484083466f278e6b392addd9f4205b48d45b5c8771670ebf8/narwhals-2.17.0.tar.gz", hash = "sha256:ebd5bc95bcfa2f8e89a8ac09e2765a63055162837208e67b42d6eeb6651d5e67", size = 620306 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.whl", hash = "sha256:cbfe21ca19d260d9fd67f995ec75c44592d1f106933b03ddd375df7ac841f9d6", size = 432856, upload-time = "2026-01-06T08:10:11.511Z" }, + { url = "https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl", hash = "sha256:2ac5307b7c2b275a7d66eeda906b8605e3d7a760951e188dcfff86e8ebe083dd", size = 444897 }, ] [[package]] @@ -1808,14 +1823,14 @@ dependencies = [ { name = "nbformat" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554, upload-time = "2025-12-23T07:45:46.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465, upload-time = "2025-12-23T07:45:44.51Z" }, + { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465 }, ] [[package]] name = "nbconvert" -version = "7.16.6" +version = "7.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, @@ -1833,9 +1848,9 @@ dependencies = [ { name = "pygments" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715, upload-time = "2025-01-28T09:29:14.724Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/47/81f886b699450d0569f7bc551df2b1673d18df7ff25cc0c21ca36ed8a5ff/nbconvert-7.17.0.tar.gz", hash = "sha256:1b2696f1b5be12309f6c7d707c24af604b87dfaf6d950794c7b07acab96dda78", size = 862855 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525, upload-time = "2025-01-28T09:29:12.551Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl", hash = "sha256:4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518", size = 261510 }, ] [[package]] @@ -1848,53 +1863,53 @@ dependencies = [ { name = "jupyter-core" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, ] [[package]] name = "nbstripout" -version = "0.8.2" +version = "0.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nbformat" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/09/cea5360a0788f94337d1b65c313ff0a448fb6a444b65cab89378eb8f7d5c/nbstripout-0.8.2.tar.gz", hash = "sha256:2876530eb684bf93a5b48fe6d92b2163f78d040721c76b37d5b9e1514d38fc69", size = 27747, upload-time = "2025-11-16T17:38:55.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/6f/b52c4da26babeb521078c08c78c3187a59197098ffc7a70b0fe76851813a/nbstripout-0.9.1.tar.gz", hash = "sha256:313bbb4217c8e38998567e5d790b6bd6c3a17a8c39073b205b84dadfc5d756dc", size = 32356 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl", hash = "sha256:5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae", size = 17122, upload-time = "2025-11-16T17:38:54.164Z" }, + { url = "https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl", hash = "sha256:ca027ee45742ee77e4f8e9080254f9a707f1161ba11367b82fdf4a29892c759e", size = 19136 }, ] [[package]] name = "nest-asyncio" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, ] [[package]] name = "networkx" version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, ] [[package]] name = "nodeenv" version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438 }, ] [[package]] name = "notebook" -version = "7.5.1" +version = "7.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, @@ -1903,9 +1918,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/a9/882707b0aa639e6d7d3e7df4bfbe07479d832e9a8f02d8471002a4ea6d65/notebook-7.5.1.tar.gz", hash = "sha256:b2fb4cef4d47d08c33aecce1c6c6e84be05436fbd791f88fce8df9fbca088b75", size = 14058696, upload-time = "2025-12-16T07:38:59.223Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/cb/cc7f4df5cee315dd126a47eb60890690a0438d5e0dd40c32d60ce16de377/notebook-7.5.3.tar.gz", hash = "sha256:393ceb269cf9fdb02a3be607a57d7bd5c2c14604f1818a17dbeb38e04f98cbfa", size = 14073140 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/86/ca516cb58ad2cb2064124d31cf0fd8b012fca64bebeb26da2d2ddf03fc79/notebook-7.5.1-py3-none-any.whl", hash = "sha256:f4e2451c19910c33b88709b84537e11f6368c1cdff1aa0c43db701aea535dd44", size = 14468080, upload-time = "2025-12-16T07:38:55.644Z" }, + { url = "https://files.pythonhosted.org/packages/96/98/9286e7f35e5584ebb79f997f2fb0cb66745c86f6c5fccf15ba32aac5e908/notebook-7.5.3-py3-none-any.whl", hash = "sha256:c997bfa1a2a9eb58c9bbb7e77d50428befb1033dd6f02c482922e96851d67354", size = 14481744 }, ] [[package]] @@ -1915,25 +1930,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, ] [[package]] @@ -1941,7 +1956,8 @@ name = "nvidia-cublas-cu12" version = "12.8.4.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, + { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, ] [[package]] @@ -1949,7 +1965,8 @@ name = "nvidia-cuda-cupti-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, ] [[package]] @@ -1957,7 +1974,8 @@ name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, + { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, ] [[package]] @@ -1965,7 +1983,8 @@ name = "nvidia-cuda-runtime-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, ] [[package]] @@ -1973,10 +1992,11 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, + { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, ] [[package]] @@ -1984,10 +2004,11 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, + { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, ] [[package]] @@ -1995,7 +2016,8 @@ name = "nvidia-cufile-cu12" version = "1.13.1.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, + { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, ] [[package]] @@ -2003,7 +2025,8 @@ name = "nvidia-curand-cu12" version = "10.3.9.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, + { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, ] [[package]] @@ -2011,12 +2034,13 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, + { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, ] [[package]] @@ -2024,10 +2048,11 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, ] [[package]] @@ -2035,7 +2060,8 @@ name = "nvidia-cusparselt-cu12" version = "0.7.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, + { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, ] [[package]] @@ -2043,7 +2069,8 @@ name = "nvidia-nccl-cu12" version = "2.27.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, ] [[package]] @@ -2051,15 +2078,17 @@ name = "nvidia-nvjitlink-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, + { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, ] [[package]] name = "nvidia-nvshmem-cu12" -version = "3.3.20" +version = "3.4.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6a/03aa43cc9bd3ad91553a88b5f6fb25ed6a3752ae86ce2180221962bc2aa5/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15", size = 138936938 }, + { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095 }, ] [[package]] @@ -2067,7 +2096,8 @@ name = "nvidia-nvtx-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, ] [[package]] @@ -2077,24 +2107,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045, upload-time = "2020-01-18T16:55:48.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045 } [[package]] name = "overrides" version = "7.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, ] [[package]] name = "packaging" -version = "25.0" +version = "26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366 }, ] [[package]] @@ -2107,33 +2137,33 @@ dependencies = [ { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, - { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, - { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, - { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, - { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, - { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763 }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217 }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791 }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373 }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444 }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459 }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086 }, ] [[package]] name = "pandocfilters" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, ] [[package]] name = "parso" -version = "0.8.5" +version = "0.8.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621 } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, + { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894 }, ] [[package]] @@ -2143,73 +2173,66 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, ] [[package]] name = "pillow" -version = "10.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload-time = "2024-07-01T09:45:22.07Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload-time = "2024-07-01T09:45:25.292Z" }, - { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload-time = "2024-07-01T09:45:27.94Z" }, - { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload-time = "2024-07-01T09:45:30.305Z" }, - { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload-time = "2024-07-01T09:45:32.868Z" }, - { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload-time = "2024-07-01T09:45:35.279Z" }, - { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload-time = "2024-07-01T09:45:37.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload-time = "2024-07-01T09:45:39.89Z" }, - { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload-time = "2024-07-01T09:45:42.771Z" }, - { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload-time = "2024-07-01T09:45:45.176Z" }, - { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload-time = "2024-07-01T09:45:47.274Z" }, - { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload-time = "2024-07-01T09:48:04.815Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload-time = "2024-07-01T09:48:07.206Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload-time = "2024-07-01T09:48:09.66Z" }, - { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload-time = "2024-07-01T09:48:12.529Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload-time = "2024-07-01T09:48:14.891Z" }, - { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload-time = "2024-07-01T09:48:17.601Z" }, - { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload-time = "2024-07-01T09:48:20.293Z" }, +version = "12.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/30/5bd3d794762481f8c8ae9c80e7b76ecea73b916959eb587521358ef0b2f9/pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0", size = 5304099 }, + { url = "https://files.pythonhosted.org/packages/bd/c1/aab9e8f3eeb4490180e357955e15c2ef74b31f64790ff356c06fb6cf6d84/pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713", size = 4657880 }, + { url = "https://files.pythonhosted.org/packages/f1/0a/9879e30d56815ad529d3985aeff5af4964202425c27261a6ada10f7cbf53/pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b", size = 6222587 }, + { url = "https://files.pythonhosted.org/packages/5a/5f/a1b72ff7139e4f89014e8d451442c74a774d5c43cd938fb0a9f878576b37/pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b", size = 8027678 }, + { url = "https://files.pythonhosted.org/packages/e2/c2/c7cb187dac79a3d22c3ebeae727abee01e077c8c7d930791dc592f335153/pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4", size = 6335777 }, + { url = "https://files.pythonhosted.org/packages/0c/7b/f9b09a7804ec7336effb96c26d37c29d27225783dc1501b7d62dcef6ae25/pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4", size = 7027140 }, + { url = "https://files.pythonhosted.org/packages/98/b2/2fa3c391550bd421b10849d1a2144c44abcd966daadd2f7c12e19ea988c4/pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e", size = 6449855 }, + { url = "https://files.pythonhosted.org/packages/96/ff/9caf4b5b950c669263c39e96c78c0d74a342c71c4f43fd031bb5cb7ceac9/pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff", size = 7151329 }, + { url = "https://files.pythonhosted.org/packages/7b/f8/4b24841f582704da675ca535935bccb32b00a6da1226820845fac4a71136/pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40", size = 6325574 }, + { url = "https://files.pythonhosted.org/packages/f8/f9/9f6b01c0881d7036063aa6612ef04c0e2cad96be21325a1e92d0203f8e91/pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23", size = 7032347 }, + { url = "https://files.pythonhosted.org/packages/79/13/c7922edded3dcdaf10c59297540b72785620abc0538872c819915746757d/pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9", size = 2453457 }, ] [[package]] name = "pip" -version = "25.3" +version = "26.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/83/0d7d4e9efe3344b8e2fe25d93be44f64b65364d3c8d7bc6dc90198d5422e/pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8", size = 1812747 } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", size = 1787723 }, ] [[package]] name = "platformdirs" -version = "4.5.1" +version = "4.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/04/fea538adf7dbbd6d186f551d595961e564a3b6715bdf276b477460858672/platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291", size = 28394 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, + { url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168 }, ] [[package]] name = "plotly" -version = "6.5.0" +version = "6.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "narwhals" }, { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624, upload-time = "2025-11-17T18:39:24.523Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/4f/8a10a9b9f5192cb6fdef62f1d77fa7d834190b2c50c0cd256bd62879212b/plotly-6.5.2.tar.gz", hash = "sha256:7478555be0198562d1435dee4c308268187553cc15516a2f4dd034453699e393", size = 7015695 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174, upload-time = "2025-11-17T18:39:20.351Z" }, + { url = "https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl", hash = "sha256:91757653bd9c550eeea2fa2404dba6b85d1e366d54804c340b2c874e5a7eb4a4", size = 9895973 }, ] [[package]] name = "pptree" version = "3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043, upload-time = "2020-04-15T18:28:53.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043 } [[package]] name = "pre-commit" @@ -2222,18 +2245,18 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437 }, ] [[package]] name = "prometheus-client" -version = "0.23.1" +version = "0.24.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481, upload-time = "2025-09-18T20:47:25.043Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, + { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057 }, ] [[package]] @@ -2243,105 +2266,105 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, ] [[package]] name = "propcache" version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, - { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, - { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, - { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, - { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, - { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, - { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, - { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, - { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, - { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534 }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526 }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263 }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012 }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491 }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319 }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856 }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241 }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552 }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778 }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047 }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093 }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638 }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229 }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, ] [[package]] name = "protobuf" -version = "6.33.2" +version = "6.33.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, - { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, - { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, - { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, - { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, - { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, + { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769 }, + { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118 }, + { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766 }, + { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638 }, + { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411 }, + { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465 }, + { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687 }, ] [[package]] name = "psutil" version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565, upload-time = "2024-10-17T21:31:45.68Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565 } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762, upload-time = "2024-10-17T21:32:05.991Z" }, - { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777, upload-time = "2024-10-17T21:32:07.872Z" }, - { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259, upload-time = "2024-10-17T21:32:10.177Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255, upload-time = "2024-10-17T21:32:11.964Z" }, - { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804, upload-time = "2024-10-17T21:32:13.785Z" }, - { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386, upload-time = "2024-10-17T21:32:21.399Z" }, - { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228, upload-time = "2024-10-17T21:32:23.88Z" }, + { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762 }, + { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777 }, + { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259 }, + { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255 }, + { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804 }, + { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386 }, + { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228 }, ] [[package]] name = "ptyprocess" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, ] [[package]] name = "pure-eval" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, ] [[package]] name = "pyarrow" -version = "22.0.0" +version = "23.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151, upload-time = "2025-10-24T12:30:00.762Z" } +sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968, upload-time = "2025-10-24T10:03:31.21Z" }, - { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085, upload-time = "2025-10-24T10:03:38.146Z" }, - { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613, upload-time = "2025-10-24T10:03:46.516Z" }, - { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059, upload-time = "2025-10-24T10:03:55.353Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043, upload-time = "2025-10-24T10:04:05.408Z" }, - { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505, upload-time = "2025-10-24T10:04:15.786Z" }, - { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641, upload-time = "2025-10-24T10:04:22.57Z" }, + { url = "https://files.pythonhosted.org/packages/bc/a8/24e5dc6855f50a62936ceb004e6e9645e4219a8065f304145d7fb8a79d5d/pyarrow-23.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56", size = 34307390 }, + { url = "https://files.pythonhosted.org/packages/bc/8e/4be5617b4aaae0287f621ad31c6036e5f63118cfca0dc57d42121ff49b51/pyarrow-23.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c", size = 35853761 }, + { url = "https://files.pythonhosted.org/packages/2e/08/3e56a18819462210432ae37d10f5c8eed3828be1d6c751b6e6a2e93c286a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258", size = 44493116 }, + { url = "https://files.pythonhosted.org/packages/f8/82/c40b68001dbec8a3faa4c08cd8c200798ac732d2854537c5449dc859f55a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2", size = 47564532 }, + { url = "https://files.pythonhosted.org/packages/20/bc/73f611989116b6f53347581b02177f9f620efdf3cd3f405d0e83cdf53a83/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5", size = 48183685 }, + { url = "https://files.pythonhosted.org/packages/b0/cc/6c6b3ecdae2a8c3aced99956187e8302fc954cc2cca2a37cf2111dad16ce/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222", size = 50605582 }, + { url = "https://files.pythonhosted.org/packages/8d/94/d359e708672878d7638a04a0448edf7c707f9e5606cee11e15aaa5c7535a/pyarrow-23.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d", size = 27521148 }, ] [[package]] name = "pycparser" -version = "2.23" +version = "3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, ] [[package]] @@ -2354,9 +2377,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 }, ] [package.optional-dependencies] @@ -2371,29 +2394,37 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, - { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, - { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, - { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, - { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, - { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, - { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, - { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, - { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, - { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, - { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, - { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, - { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, - { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, - { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, - { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, - { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298 }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475 }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815 }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567 }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442 }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956 }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253 }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050 }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178 }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833 }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156 }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378 }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622 }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441 }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291 }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632 }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905 }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495 }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388 }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879 }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017 }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351 }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363 }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615 }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369 }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218 }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951 }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428 }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009 }, ] [[package]] @@ -2404,87 +2435,87 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, + { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296 }, ] [[package]] name = "pydantic-settings" -version = "2.12.0" +version = "2.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, + { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929 }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] [[package]] name = "pymupdf" -version = "1.26.7" +version = "1.27.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/d6/09b28f027b510838559f7748807192149c419b30cb90e6d5f0cf916dc9dc/pymupdf-1.26.7.tar.gz", hash = "sha256:71add8bdc8eb1aaa207c69a13400693f06ad9b927bea976f5d5ab9df0bb489c3", size = 84327033, upload-time = "2025-12-11T21:48:50.694Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/0c/40dda0cc4bd2220a2ef75f8c53dd7d8ed1e29681fcb3df75db6ee9677a7e/pymupdf-1.27.1.tar.gz", hash = "sha256:4afbde0769c336717a149ab0de3330dcb75378f795c1a8c5af55c1a628b17d55", size = 85303479 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/35/cd74cea1787b2247702ef8522186bdef32e9cb30a099e6bb864627ef6045/pymupdf-1.26.7-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:07085718dfdae5ab83b05eb5eb397f863bcc538fe05135318a01ea353e7a1353", size = 23179369, upload-time = "2025-12-11T21:47:21.587Z" }, - { url = "https://files.pythonhosted.org/packages/72/74/448b6172927c829c6a3fba80078d7b0a016ebbe2c9ee528821f5ea21677a/pymupdf-1.26.7-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:31aa9c8377ea1eea02934b92f4dcf79fb2abba0bf41f8a46d64c3e31546a3c02", size = 22470101, upload-time = "2025-12-11T21:47:37.105Z" }, - { url = "https://files.pythonhosted.org/packages/65/e7/47af26f3ac76be7ac3dd4d6cc7ee105948a8355d774e5ca39857bf91c11c/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e419b609996434a14a80fa060adec72c434a1cca6a511ec54db9841bc5d51b3c", size = 23502486, upload-time = "2025-12-12T09:51:25.824Z" }, - { url = "https://files.pythonhosted.org/packages/2a/6b/3de1714d734ff949be1e90a22375d0598d3540b22ae73eb85c2d7d1f36a9/pymupdf-1.26.7-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:69dfc78f206a96e5b3ac22741263ebab945fdf51f0dbe7c5757c3511b23d9d72", size = 24115727, upload-time = "2025-12-11T21:47:51.274Z" }, - { url = "https://files.pythonhosted.org/packages/62/9b/f86224847949577a523be2207315ae0fd3155b5d909cd66c274d095349a3/pymupdf-1.26.7-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1d5106f46e1ca0d64d46bd51892372a4f82076bdc14a9678d33d630702abca36", size = 24324386, upload-time = "2025-12-12T14:58:45.483Z" }, - { url = "https://files.pythonhosted.org/packages/85/8e/a117d39092ca645fde8b903f4a941d9aa75b370a67b4f1f435f56393dc5a/pymupdf-1.26.7-cp310-abi3-win32.whl", hash = "sha256:7c9645b6f5452629c747690190350213d3e5bbdb6b2eca227d82702b327f6eee", size = 17203888, upload-time = "2025-12-12T13:59:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c3/d0047678146c294469c33bae167c8ace337deafb736b0bf97b9bc481aa65/pymupdf-1.26.7-cp310-abi3-win_amd64.whl", hash = "sha256:425b1befe40d41b72eb0fe211711c7ae334db5eb60307e9dd09066ed060cceba", size = 18405952, upload-time = "2025-12-11T21:48:02.947Z" }, + { url = "https://files.pythonhosted.org/packages/13/19/fde6ea4712a904b65e8f41124a0e4233879b87a770fe6a8ce857964de6d5/pymupdf-1.27.1-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:bee9f95512f9556dbf2cacfd1413c61b29a55baa07fa7f8fc83d221d8419888a", size = 23986707 }, + { url = "https://files.pythonhosted.org/packages/75/c2/070dff91ad3f1bc16fd6c6ceff23495601fcce4c92d28be534417596418a/pymupdf-1.27.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:3de95a0889395b0966fafd11b94980b7543a816e89dd1c218597a08543ac3415", size = 23263493 }, + { url = "https://files.pythonhosted.org/packages/8e/db/937377f4b3e0fbf6273c17436a49f7db17df1a46b1be9e26653b6fafc0e1/pymupdf-1.27.1-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2c9d9353b840040cbc724341f4095fb7e2cc1a12a9147d0ec1a0a79f5d773147", size = 24317651 }, + { url = "https://files.pythonhosted.org/packages/72/d5/c701cf2d0cdd6e5d6bca3ca9188d7f5d7ce3ae67dd1368d658cd4bae2707/pymupdf-1.27.1-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:aeaed76e72cbc061149a825ab0811c5f4752970c56591c2938c5042ec06b26e1", size = 24945742 }, + { url = "https://files.pythonhosted.org/packages/2b/29/690202b38b93cf77b73a29c25a63a2b6f3fcb36b1f75006e50b8dee7c108/pymupdf-1.27.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4f1837554134fb45d390a44de8844b2ca9b6c901c82ccc90b340e3b7f3b126ca", size = 25167965 }, + { url = "https://files.pythonhosted.org/packages/8a/81/f937e6aa606fd263c3a45d0ff0f0bbdbf3fb779933091fc0f6179513cc93/pymupdf-1.27.1-cp310-abi3-win32.whl", hash = "sha256:fa33b512d82c6c4852edadf57f22d5f27d16243bb33dac0fbe4eb0f281c5b17e", size = 18006253 }, + { url = "https://files.pythonhosted.org/packages/3e/99/fe4a7752990bf65277718fffbead4478de9afd1c7288d7a6d643f79a6fa7/pymupdf-1.27.1-cp310-abi3-win_amd64.whl", hash = "sha256:4b6268dff3a9d713034eba5c2ffce0da37c62443578941ac5df433adcde57b2f", size = 19236703 }, ] [[package]] name = "pymupdf4llm" -version = "0.2.8" +version = "0.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pymupdf" }, { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/ff/d2e856ff5db5fa57ebf13ae04de4da6c203f5fb17507c0dd21ffd0ae4a17/pymupdf4llm-0.2.8.tar.gz", hash = "sha256:c4f9815cff210cef45123e93ae3a6f802e0dc1d1ad4534a4a463c000aaec98fd", size = 66867, upload-time = "2026-01-04T16:38:30.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/9b/97c4fad642f0147af3e884126d80635a1fae02c836fde9c22f0efc053254/pymupdf4llm-0.3.4.tar.gz", hash = "sha256:48d396a5fb3c14351493c7f1dd25b2a843efdbdc4526e489ee100643a2cebec1", size = 74956 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/d5/47e14346b6f57cbc7456cc24ded697b4037b399ff56ed12afccbd202361d/pymupdf4llm-0.2.8-py3-none-any.whl", hash = "sha256:e60587d151223fc9e118661a9c7bb04b04ae928187c201a14d60ac8a42963591", size = 68637, upload-time = "2026-01-04T16:38:32.412Z" }, + { url = "https://files.pythonhosted.org/packages/2b/67/4394de2e5967d80a9b01ec323049163410b3553185c42dea0c346fa27a41/pymupdf4llm-0.3.4-py3-none-any.whl", hash = "sha256:0517492f82af978541162ade20fc54649cdca52acd478e33b97cb6171d69956f", size = 78669 }, ] [[package]] name = "pypandoc" version = "1.16.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477, upload-time = "2025-11-13T16:30:29.608Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451, upload-time = "2025-11-13T16:30:07.66Z" }, + { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451 }, ] [[package]] name = "pyparsing" -version = "3.3.1" +version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/c1/1d9de9aeaa1b89b0186e5fe23294ff6517fce1bc69149185577cd31016b2/pyparsing-3.3.1.tar.gz", hash = "sha256:47fad0f17ac1e2cad3de3b458570fbc9b03560aa029ed5e16ee5554da9a2251c", size = 1550512, upload-time = "2025-12-23T03:14:04.391Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-py3-none-any.whl", hash = "sha256:023b5e7e5520ad96642e2c6db4cb683d3970bd640cdf7115049a6e9c3682df82", size = 121793, upload-time = "2025-12-23T03:14:02.103Z" }, + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781 }, ] [[package]] name = "pysocks" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725 }, ] [[package]] @@ -2494,9 +2525,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] [[package]] @@ -2507,36 +2538,36 @@ dependencies = [ { name = "lxml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987 }, ] [[package]] name = "python-dotenv" version = "1.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, ] [[package]] name = "python-json-logger" version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548 }, ] [[package]] name = "python-multipart" -version = "0.0.21" +version = "0.0.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/78/96/804520d0850c7db98e5ccb70282e29208723f0964e88ffd9d0da2f52ea09/python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92", size = 37196, upload-time = "2025-12-17T09:24:22.446Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612 } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/76/03af049af4dcee5d27442f71b6924f01f3efb5d2bd34f23fcd563f2cc5f5/python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090", size = 24541, upload-time = "2025-12-17T09:24:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579 }, ] [[package]] @@ -2555,9 +2586,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889, upload-time = "2022-11-25T19:33:07.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949, upload-time = "2022-11-25T19:33:06.093Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949 }, ] [[package]] @@ -2568,44 +2599,45 @@ dependencies = [ { name = "numpy" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086, upload-time = "2021-01-09T17:35:49.131Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564, upload-time = "2021-01-09T17:35:47.543Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564 }, ] [[package]] name = "pytz" version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, ] [[package]] name = "pywinpty" -version = "3.0.2" +version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/bb/a7cc2967c5c4eceb6cc49cfe39447d4bfc56e6c865e7c2249b6eb978935f/pywinpty-3.0.2.tar.gz", hash = "sha256:1505cc4cb248af42cb6285a65c9c2086ee9e7e574078ee60933d5d7fa86fb004", size = 30669, upload-time = "2025-10-03T21:16:29.205Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/f5/b17ae550841949c217ad557ee445b4a14e9c0b506ae51ee087eff53428a6/pywinpty-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:65db57fd3387d71e8372b6a54269cbcd0f6dfa6d4616a29e0af749ec19f5c558", size = 2050330, upload-time = "2025-10-03T21:20:15.656Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/a652709bd76ca7533cd1c443b03add9f5051fdf71bc6bdb8801dddd4e7a3/pywinpty-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:ff05f12d775b142b11c6fe085129bdd759b61cf7d41da6c745e78e3a1ef5bf40", size = 2114320 }, + { url = "https://files.pythonhosted.org/packages/b2/13/a0181cc5c2d5635d3dbc3802b97bc8e3ad4fa7502ccef576651a5e08e54c/pywinpty-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:340ccacb4d74278a631923794ccd758471cfc8eeeeee4610b280420a17ad1e82", size = 235670 }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227 }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019 }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646 }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793 }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293 }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872 }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828 }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415 }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561 }, ] [[package]] @@ -2615,52 +2647,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850, upload-time = "2025-09-08T23:07:26.274Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380, upload-time = "2025-09-08T23:07:29.78Z" }, - { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421, upload-time = "2025-09-08T23:07:31.263Z" }, - { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149, upload-time = "2025-09-08T23:07:33.17Z" }, - { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070, upload-time = "2025-09-08T23:07:35.205Z" }, - { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441, upload-time = "2025-09-08T23:07:37.432Z" }, - { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529, upload-time = "2025-09-08T23:07:39.047Z" }, - { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276, upload-time = "2025-09-08T23:07:40.695Z" }, - { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208, upload-time = "2025-09-08T23:07:42.298Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766, upload-time = "2025-09-08T23:07:43.869Z" }, - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, - { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266, upload-time = "2025-09-08T23:09:40.048Z" }, - { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206, upload-time = "2025-09-08T23:09:41.902Z" }, - { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747, upload-time = "2025-09-08T23:09:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371, upload-time = "2025-09-08T23:09:45.575Z" }, - { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862, upload-time = "2025-09-08T23:09:47.448Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850 }, + { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380 }, + { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421 }, + { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149 }, + { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070 }, + { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441 }, + { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529 }, + { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276 }, + { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208 }, + { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766 }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, + { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266 }, + { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206 }, + { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747 }, + { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371 }, + { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862 }, ] [[package]] name = "rapidfuzz" version = "3.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900, upload-time = "2025-11-01T11:54:52.321Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376, upload-time = "2025-11-01T11:52:29.175Z" }, - { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903, upload-time = "2025-11-01T11:52:31.239Z" }, - { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655, upload-time = "2025-11-01T11:52:32.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708, upload-time = "2025-11-01T11:52:34.618Z" }, - { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106, upload-time = "2025-11-01T11:52:36.069Z" }, - { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048, upload-time = "2025-11-01T11:52:37.936Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020, upload-time = "2025-11-01T11:52:39.657Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958, upload-time = "2025-11-01T11:52:41.017Z" }, - { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043, upload-time = "2025-11-01T11:52:42.465Z" }, - { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273, upload-time = "2025-11-01T11:52:44.005Z" }, - { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875, upload-time = "2025-11-01T11:52:45.405Z" }, + { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376 }, + { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903 }, + { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655 }, + { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708 }, + { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106 }, + { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048 }, + { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020 }, + { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958 }, + { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043 }, + { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273 }, + { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875 }, ] [[package]] @@ -2672,33 +2704,34 @@ dependencies = [ { name = "rpds-py" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, ] [[package]] name = "regex" -version = "2024.11.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674, upload-time = "2024-11-06T20:08:57.575Z" }, - { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684, upload-time = "2024-11-06T20:08:59.787Z" }, - { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589, upload-time = "2024-11-06T20:09:01.896Z" }, - { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511, upload-time = "2024-11-06T20:09:04.062Z" }, - { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149, upload-time = "2024-11-06T20:09:06.237Z" }, - { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707, upload-time = "2024-11-06T20:09:07.715Z" }, - { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702, upload-time = "2024-11-06T20:09:10.101Z" }, - { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976, upload-time = "2024-11-06T20:09:11.566Z" }, - { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397, upload-time = "2024-11-06T20:09:13.119Z" }, - { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726, upload-time = "2024-11-06T20:09:14.85Z" }, - { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098, upload-time = "2024-11-06T20:09:16.504Z" }, - { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325, upload-time = "2024-11-06T20:09:18.698Z" }, - { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277, upload-time = "2024-11-06T20:09:21.725Z" }, - { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197, upload-time = "2024-11-06T20:09:24.092Z" }, - { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714, upload-time = "2024-11-06T20:09:26.36Z" }, - { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042, upload-time = "2024-11-06T20:09:28.762Z" }, +version = "2026.2.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/c0/d8079d4f6342e4cec5c3e7d7415b5cd3e633d5f4124f7a4626908dbe84c7/regex-2026.2.19.tar.gz", hash = "sha256:6fb8cb09b10e38f3ae17cc6dc04a1df77762bd0351b6ba9041438e7cc85ec310", size = 414973 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/de/f10b4506acfd684de4e42b0aa56ccea1a778a18864da8f6d319a40591062/regex-2026.2.19-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f5a37a17d110f9d5357a43aa7e3507cb077bf3143d1c549a45c4649e90e40a70", size = 488369 }, + { url = "https://files.pythonhosted.org/packages/8b/2f/b4eaef1f0b4d0bf2a73eaf07c08f6c13422918a4180c9211ce0521746d0c/regex-2026.2.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:676c4e6847a83a1d5732b4ed553881ad36f0a8133627bb695a89ecf3571499d3", size = 290743 }, + { url = "https://files.pythonhosted.org/packages/76/7c/805413bd0a88d04688c0725c222cfb811bd54a2f571004c24199a1ae55d6/regex-2026.2.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82336faeecac33297cd42857c3b36f12b91810e3fdd276befdd128f73a2b43fa", size = 288652 }, + { url = "https://files.pythonhosted.org/packages/08/ff/2c4cd530a878b1975398e76faef4285f11e7c9ccf1aaedfd528bfcc1f580/regex-2026.2.19-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52136f5b71f095cb74b736cc3a1b578030dada2e361ef2f07ca582240b703946", size = 781759 }, + { url = "https://files.pythonhosted.org/packages/37/45/9608ab1b41f6740ff4076eabadde8e8b3f3400942b348ac41e8599ccc131/regex-2026.2.19-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4192464fe3e6cb0ef6751f7d3b16f886d8270d359ed1590dd555539d364f0ff7", size = 850947 }, + { url = "https://files.pythonhosted.org/packages/90/3a/66471b6c4f7cac17e14bf5300e46661bba2b17ffb0871bd2759e837a6f82/regex-2026.2.19-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e561dd47a85d2660d3d3af4e6cb2da825cf20f121e577147963f875b83d32786", size = 898794 }, + { url = "https://files.pythonhosted.org/packages/c2/d2/38c53929a5931f7398e5e49f5a5a3079cb2aba30119b4350608364cfad8c/regex-2026.2.19-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00ec994d7824bf01cd6c7d14c7a6a04d9aeaf7c42a2bc22d2359d715634d539b", size = 791922 }, + { url = "https://files.pythonhosted.org/packages/8b/bd/b046e065630fa25059d9c195b7b5308ea94da45eee65d40879772500f74c/regex-2026.2.19-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2cb00aabd96b345d56a8c2bc328c8d6c4d29935061e05078bf1f02302e12abf5", size = 783345 }, + { url = "https://files.pythonhosted.org/packages/d4/8f/045c643d2fa255a985e8f87d848e4be230b711a8935e4bdc58e60b8f7b84/regex-2026.2.19-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f374366ed35673ea81b86a8859c457d4fae6ba092b71024857e9e237410c7404", size = 768055 }, + { url = "https://files.pythonhosted.org/packages/72/9f/ab7ae9f5447559562f1a788bbc85c0e526528c5e6c20542d18e4afc86aad/regex-2026.2.19-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f9417fd853fcd00b7d55167e692966dd12d95ba1a88bf08a62002ccd85030790", size = 774955 }, + { url = "https://files.pythonhosted.org/packages/37/5c/f16fc23c56f60b6f4ff194604a6e53bb8aec7b6e8e4a23a482dee8d77235/regex-2026.2.19-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:12e86a01594031abf892686fcb309b041bf3de3d13d99eb7e2b02a8f3c687df1", size = 846010 }, + { url = "https://files.pythonhosted.org/packages/51/c8/6be4c854135d7c9f35d4deeafdaf124b039ecb4ffcaeb7ed0495ad2c97ca/regex-2026.2.19-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:79014115e6fdf18fd9b32e291d58181bf42d4298642beaa13fd73e69810e4cb6", size = 755938 }, + { url = "https://files.pythonhosted.org/packages/d6/8d/f683d49b9663a5324b95a328e69d397f6dade7cb84154eec116bf79fe150/regex-2026.2.19-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:31aefac2506967b7dd69af2c58eca3cc8b086d4110b66d6ac6e9026f0ee5b697", size = 835773 }, + { url = "https://files.pythonhosted.org/packages/16/cd/619224b90da09f167fe4497c350a0d0b30edc539ee9244bf93e604c073c3/regex-2026.2.19-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:49cef7bb2a491f91a8869c7cdd90babf0a417047ab0bf923cd038ed2eab2ccb8", size = 780075 }, + { url = "https://files.pythonhosted.org/packages/5b/88/19cfb0c262d6f9d722edef29157125418bf90eb3508186bf79335afeedae/regex-2026.2.19-cp310-cp310-win32.whl", hash = "sha256:3a039474986e7a314ace6efb9ce52f5da2bdb80ac4955358723d350ec85c32ad", size = 266004 }, + { url = "https://files.pythonhosted.org/packages/82/af/5b487e0287ef72545d7ae92edecdacbe3d44e531cac24fda7de5598ba8dd/regex-2026.2.19-cp310-cp310-win_amd64.whl", hash = "sha256:5b81ff4f9cad99f90c807a00c5882fbcda86d8b3edd94e709fb531fc52cb3d25", size = 277895 }, + { url = "https://files.pythonhosted.org/packages/4c/19/b6715a187ffca4d2979af92a46ce922445ba41f910bf187ccd666a2d52ef/regex-2026.2.19-cp310-cp310-win_arm64.whl", hash = "sha256:a032bc01a4bc73fc3cadba793fce28eb420da39338f47910c59ffcc11a5ba5ef", size = 270465 }, ] [[package]] @@ -2711,9 +2744,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, ] [package.optional-dependencies] @@ -2728,18 +2761,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, ] [[package]] name = "rfc3986-validator" version = "0.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, ] [[package]] @@ -2749,92 +2782,92 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lark" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, ] [[package]] name = "rich" -version = "14.2.0" +version = "14.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458 }, ] [[package]] name = "rich-toolkit" -version = "0.17.1" +version = "0.19.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/09/3f9b8d9daaf235195c626f21e03604c05b987404ee3bcacee0c1f67f2a8e/rich_toolkit-0.17.1.tar.gz", hash = "sha256:5af54df8d1dd9c8530e462e1bdcaed625c9b49f5a55b035aa0ba1c17bdb87c9a", size = 187925, upload-time = "2025-12-17T10:49:22.583Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/c9/4bbf4bfee195ed1b7d7a6733cc523ca61dbfb4a3e3c12ea090aaffd97597/rich_toolkit-0.19.4.tar.gz", hash = "sha256:52e23d56f9dc30d1343eb3b3f6f18764c313fbfea24e52e6a1d6069bec9c18eb", size = 193951 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/7b/15e55fa8a76d0d41bf34d965af78acdaf80a315907adb30de8b63c272694/rich_toolkit-0.17.1-py3-none-any.whl", hash = "sha256:96d24bb921ecd225ffce7c526a9149e74006410c05e6d405bd74ffd54d5631ed", size = 31412, upload-time = "2025-12-17T10:49:21.793Z" }, + { url = "https://files.pythonhosted.org/packages/28/31/97d39719def09c134385bfcfbedfed255168b571e7beb3ad7765aae660ca/rich_toolkit-0.19.4-py3-none-any.whl", hash = "sha256:34ac344de8862801644be8b703e26becf44b047e687f208d7829e8f7cfc311d6", size = 32757 }, ] [[package]] name = "rignore" version = "0.7.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999, upload-time = "2025-11-05T20:42:38.373Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824, upload-time = "2025-11-05T20:42:22.1Z" }, - { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121, upload-time = "2025-11-05T20:40:48.94Z" }, - { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813, upload-time = "2025-11-05T20:41:04.71Z" }, - { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019, upload-time = "2025-11-05T20:41:20.723Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822, upload-time = "2025-11-05T20:41:36.99Z" }, - { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820, upload-time = "2025-11-05T20:42:06.364Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050, upload-time = "2025-11-05T20:41:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164, upload-time = "2025-11-05T21:40:10.368Z" }, - { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028, upload-time = "2025-11-05T21:40:27.977Z" }, - { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024, upload-time = "2025-11-05T21:40:45.148Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531, upload-time = "2025-11-05T21:41:02.734Z" }, - { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817, upload-time = "2025-11-05T21:41:47.51Z" }, - { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001, upload-time = "2025-11-05T21:41:32.778Z" }, - { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462, upload-time = "2025-11-05T20:42:50.804Z" }, - { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918, upload-time = "2025-11-05T20:42:33.689Z" }, - { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922, upload-time = "2025-11-05T20:41:00.361Z" }, - { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987, upload-time = "2025-11-05T20:41:16.219Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110, upload-time = "2025-11-05T20:41:32.631Z" }, - { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339, upload-time = "2025-11-05T20:41:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680, upload-time = "2025-11-05T20:42:18.061Z" }, - { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045, upload-time = "2025-11-05T20:42:02.315Z" }, - { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310, upload-time = "2025-11-05T21:40:23.184Z" }, - { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998, upload-time = "2025-11-05T21:40:40.603Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178, upload-time = "2025-11-05T21:40:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190, upload-time = "2025-11-05T21:41:16.403Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999 }, + { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824 }, + { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121 }, + { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813 }, + { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019 }, + { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822 }, + { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820 }, + { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050 }, + { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164 }, + { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028 }, + { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024 }, + { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531 }, + { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817 }, + { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001 }, + { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462 }, + { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918 }, + { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922 }, + { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987 }, + { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110 }, + { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339 }, + { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680 }, + { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045 }, + { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310 }, + { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998 }, + { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178 }, + { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190 }, ] [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, - { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, - { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, - { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, - { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, - { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, - { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, - { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, - { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, - { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, - { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, - { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, - { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490 }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751 }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696 }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136 }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699 }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022 }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522 }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579 }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305 }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503 }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322 }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792 }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901 }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823 }, ] [[package]] @@ -2844,35 +2877,35 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830 }, ] [[package]] name = "safetensors" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, - { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, - { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, - { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430 }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977 }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890 }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885 }, ] [[package]] @@ -2885,13 +2918,13 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, - { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, - { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, - { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221 }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834 }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938 }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818 }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969 }, ] [[package]] @@ -2901,16 +2934,16 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870, upload-time = "2024-06-24T20:35:18.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226, upload-time = "2024-06-24T20:31:50.451Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893, upload-time = "2024-06-24T20:31:57.337Z" }, - { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258, upload-time = "2024-06-24T20:32:02.711Z" }, - { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715, upload-time = "2024-06-24T20:32:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038, upload-time = "2024-06-24T20:32:17.305Z" }, - { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959, upload-time = "2024-06-24T20:32:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514, upload-time = "2024-06-24T20:32:32.618Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252, upload-time = "2024-06-24T20:32:45.06Z" }, + { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226 }, + { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893 }, + { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258 }, + { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715 }, + { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038 }, + { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959 }, + { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514 }, + { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252 }, ] [[package]] @@ -2922,9 +2955,9 @@ dependencies = [ { name = "numpy" }, { name = "pandas" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, ] [[package]] @@ -2934,26 +2967,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "regex" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244, upload-time = "2021-12-15T21:56:14.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332, upload-time = "2021-12-15T21:56:12.508Z" }, + { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332 }, ] [[package]] name = "send2trash" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/6e/421803dec0c0dfbf5a27e66491ebe6643a461e4f90422f00ea4c68ae24aa/send2trash-2.0.0.tar.gz", hash = "sha256:1761421da3f9930bfe51ed7c45343948573383ad4c27e3acebc91be324e7770d", size = 17206, upload-time = "2025-12-31T04:12:48.664Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl", hash = "sha256:e70d5ce41dbb890882cc78bc25d137478330b39a391e756fadf82e34da4d85b8", size = 17642, upload-time = "2025-12-31T04:12:45.336Z" }, + { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610 }, ] [[package]] name = "sentence-transformers" -version = "5.2.0" +version = "5.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, + { name = "numpy" }, { name = "scikit-learn" }, { name = "scipy" }, { name = "torch" }, @@ -2961,110 +2995,110 @@ dependencies = [ { name = "transformers" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/a1/64e7b111e753307ffb7c5b6d039c52d4a91a47fa32a7f5bc377a49b22402/sentence_transformers-5.2.0.tar.gz", hash = "sha256:acaeb38717de689f3dab45d5e5a02ebe2f75960a4764ea35fea65f58a4d3019f", size = 381004, upload-time = "2025-12-11T14:12:31.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/30/21664028fc0776eb1ca024879480bbbab36f02923a8ff9e4cae5a150fa35/sentence_transformers-5.2.3.tar.gz", hash = "sha256:3cd3044e1f3fe859b6a1b66336aac502eaae5d3dd7d5c8fc237f37fbf58137c7", size = 381623 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/d0/3b2897ef6a0c0c801e9fecca26bcc77081648e38e8c772885ebdd8d7d252/sentence_transformers-5.2.0-py3-none-any.whl", hash = "sha256:aa57180f053687d29b08206766ae7db549be5074f61849def7b17bf0b8025ca2", size = 493748, upload-time = "2025-12-11T14:12:29.516Z" }, + { url = "https://files.pythonhosted.org/packages/46/9f/dba4b3e18ebbe1eaa29d9f1764fbc7da0cd91937b83f2b7928d15c5d2d36/sentence_transformers-5.2.3-py3-none-any.whl", hash = "sha256:6437c62d4112b615ddebda362dfc16a4308d604c5b68125ed586e3e95d5b2e30", size = 494225 }, ] [[package]] name = "sentencepiece" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106, upload-time = "2024-02-19T17:06:47.428Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979, upload-time = "2024-02-19T17:05:34.651Z" }, - { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845, upload-time = "2024-02-19T17:05:37.371Z" }, - { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472, upload-time = "2024-02-19T17:05:39.775Z" }, - { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151, upload-time = "2024-02-19T17:05:42.594Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931, upload-time = "2024-02-19T17:05:44.695Z" }, - { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537, upload-time = "2024-02-19T17:05:46.713Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747, upload-time = "2024-02-19T17:05:48.705Z" }, - { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525, upload-time = "2024-02-19T17:05:55.145Z" }, + { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979 }, + { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845 }, + { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472 }, + { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151 }, + { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931 }, + { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537 }, + { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747 }, + { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525 }, ] [[package]] name = "sentry-sdk" -version = "2.48.0" +version = "2.53.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f0/0e9dc590513d5e742d7799e2038df3a05167cba084c6ca4f3cdd75b55164/sentry_sdk-2.48.0.tar.gz", hash = "sha256:5213190977ff7fdff8a58b722fb807f8d5524a80488626ebeda1b5676c0c1473", size = 384828, upload-time = "2025-12-16T14:55:41.722Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/06/66c8b705179bc54087845f28fd1b72f83751b6e9a195628e2e9af9926505/sentry_sdk-2.53.0.tar.gz", hash = "sha256:6520ef2c4acd823f28efc55e43eb6ce2e6d9f954a95a3aa96b6fd14871e92b77", size = 412369 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/19/8d77f9992e5cbfcaa9133c3bf63b4fbbb051248802e1e803fed5c552fbb2/sentry_sdk-2.48.0-py2.py3-none-any.whl", hash = "sha256:6b12ac256769d41825d9b7518444e57fa35b5642df4c7c5e322af4d2c8721172", size = 414555, upload-time = "2025-12-16T14:55:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/47/d4/2fdf854bc3b9c7f55219678f812600a20a138af2dd847d99004994eada8f/sentry_sdk-2.53.0-py2.py3-none-any.whl", hash = "sha256:46e1ed8d84355ae54406c924f6b290c3d61f4048625989a723fd622aab838899", size = 437908 }, ] [[package]] name = "setuptools" -version = "80.9.0" +version = "82.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/f3/748f4d6f65d1756b9ae577f329c951cda23fb900e4de9f70900ced962085/setuptools-82.0.0.tar.gz", hash = "sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb", size = 1144893 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, + { url = "https://files.pythonhosted.org/packages/e1/c6/76dc613121b793286a3f91621d7b75a2b493e0390ddca50f11993eadf192/setuptools-82.0.0-py3-none-any.whl", hash = "sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0", size = 1003468 }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, ] [[package]] name = "soupsieve" -version = "2.8.1" +version = "2.8.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/23/adf3796d740536d63a6fbda113d07e60c734b6ed5d3058d1e47fc0495e47/soupsieve-2.8.1.tar.gz", hash = "sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350", size = 117856, upload-time = "2025-12-18T13:50:34.655Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627 } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl", hash = "sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434", size = 36710, upload-time = "2025-12-18T13:50:33.267Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016 }, ] [[package]] name = "sqlalchemy" -version = "2.0.45" +version = "2.0.46" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/f9/5e4491e5ccf42f5d9cfc663741d261b3e6e1683ae7812114e7636409fcc6/sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88", size = 9869912, upload-time = "2025-12-09T21:05:16.737Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/aa/9ce0f3e7a9829ead5c8ce549392f33a12c4555a6c0609bb27d882e9c7ddf/sqlalchemy-2.0.46.tar.gz", hash = "sha256:cf36851ee7219c170bb0793dbc3da3e80c582e04a5437bc601bfe8c85c9216d7", size = 9865393 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/70/75b1387d72e2847220441166c5eb4e9846dd753895208c13e6d66523b2d9/sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85", size = 2154148, upload-time = "2025-12-10T20:03:21.023Z" }, - { url = "https://files.pythonhosted.org/packages/d8/a4/7805e02323c49cb9d1ae5cd4913b28c97103079765f520043f914fca4cb3/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4", size = 3233051, upload-time = "2025-12-09T22:06:04.768Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ec/32ae09139f61bef3de3142e85c47abdee8db9a55af2bb438da54a4549263/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0", size = 3232781, upload-time = "2025-12-09T22:09:54.435Z" }, - { url = "https://files.pythonhosted.org/packages/ad/bd/bf7b869b6f5585eac34222e1cf4405f4ba8c3b85dd6b1af5d4ce8bca695f/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0", size = 3182096, upload-time = "2025-12-09T22:06:06.169Z" }, - { url = "https://files.pythonhosted.org/packages/21/6a/c219720a241bb8f35c88815ccc27761f5af7fdef04b987b0e8a2c1a6dcaa/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826", size = 3205109, upload-time = "2025-12-09T22:09:55.969Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c4/6ccf31b2bc925d5d95fab403ffd50d20d7c82b858cf1a4855664ca054dce/sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a", size = 2114240, upload-time = "2025-12-09T21:29:54.007Z" }, - { url = "https://files.pythonhosted.org/packages/de/29/a27a31fca07316def418db6f7c70ab14010506616a2decef1906050a0587/sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7", size = 2137615, upload-time = "2025-12-09T21:29:55.85Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672, upload-time = "2025-12-09T21:54:52.608Z" }, + { url = "https://files.pythonhosted.org/packages/40/26/66ba59328dc25e523bfcb0f8db48bdebe2035e0159d600e1f01c0fc93967/sqlalchemy-2.0.46-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:895296687ad06dc9b11a024cf68e8d9d3943aa0b4964278d2553b86f1b267735", size = 2155051 }, + { url = "https://files.pythonhosted.org/packages/21/cd/9336732941df972fbbfa394db9caa8bb0cf9fe03656ec728d12e9cbd6edc/sqlalchemy-2.0.46-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab65cb2885a9f80f979b85aa4e9c9165a31381ca322cbde7c638fe6eefd1ec39", size = 3234666 }, + { url = "https://files.pythonhosted.org/packages/38/62/865ae8b739930ec433cd4123760bee7f8dafdc10abefd725a025604fb0de/sqlalchemy-2.0.46-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52fe29b3817bd191cc20bad564237c808967972c97fa683c04b28ec8979ae36f", size = 3232917 }, + { url = "https://files.pythonhosted.org/packages/24/38/805904b911857f2b5e00fdea44e9570df62110f834378706939825579296/sqlalchemy-2.0.46-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:09168817d6c19954d3b7655da6ba87fcb3a62bb575fb396a81a8b6a9fadfe8b5", size = 3185790 }, + { url = "https://files.pythonhosted.org/packages/69/4f/3260bb53aabd2d274856337456ea52f6a7eccf6cce208e558f870cec766b/sqlalchemy-2.0.46-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:be6c0466b4c25b44c5d82b0426b5501de3c424d7a3220e86cd32f319ba56798e", size = 3207206 }, + { url = "https://files.pythonhosted.org/packages/ce/b3/67c432d7f9d88bb1a61909b67e29f6354d59186c168fb5d381cf438d3b73/sqlalchemy-2.0.46-cp310-cp310-win32.whl", hash = "sha256:1bc3f601f0a818d27bfe139f6766487d9c88502062a2cd3a7ee6c342e81d5047", size = 2115296 }, + { url = "https://files.pythonhosted.org/packages/4a/8c/25fb284f570f9d48e6c240f0269a50cec9cf009a7e08be4c0aaaf0654972/sqlalchemy-2.0.46-cp310-cp310-win_amd64.whl", hash = "sha256:e0c05aff5c6b1bb5fb46a87e0f9d2f733f83ef6cbbbcd5c642b6c01678268061", size = 2138540 }, + { url = "https://files.pythonhosted.org/packages/fc/a1/9c4efa03300926601c19c18582531b45aededfb961ab3c3585f1e24f120b/sqlalchemy-2.0.46-py3-none-any.whl", hash = "sha256:f9c11766e7e7c0a2767dda5acb006a118640c9fc0a4104214b96269bfb78399e", size = 1937882 }, ] [[package]] name = "sqlitedict" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846, upload-time = "2022-12-03T13:39:13.102Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846 } [[package]] name = "sqlmodel" @@ -3074,9 +3108,9 @@ dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392, upload-time = "2024-08-31T09:43:24.088Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276, upload-time = "2024-08-31T09:43:22.358Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, ] [[package]] @@ -3088,22 +3122,22 @@ dependencies = [ { name = "executing" }, { name = "pure-eval" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, ] [[package]] name = "starlette" -version = "0.50.0" +version = "0.52.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, + { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272 }, ] [[package]] @@ -3113,27 +3147,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, ] [[package]] name = "tabulate" version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, ] [[package]] name = "tenacity" -version = "9.1.2" +version = "9.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926 }, ] [[package]] @@ -3145,18 +3179,18 @@ dependencies = [ { name = "packaging" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2b/c5/d4cc6e293fb837aaf9f76dd7745476aeba8ef7ef5146c3b3f9ee375fe7a5/tensorboardx-2.6.4.tar.gz", hash = "sha256:b163ccb7798b31100b9f5fa4d6bc22dad362d7065c2f24b51e50731adde86828", size = 4769801, upload-time = "2025-06-10T22:37:07.419Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/c5/d4cc6e293fb837aaf9f76dd7745476aeba8ef7ef5146c3b3f9ee375fe7a5/tensorboardx-2.6.4.tar.gz", hash = "sha256:b163ccb7798b31100b9f5fa4d6bc22dad362d7065c2f24b51e50731adde86828", size = 4769801 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/1d/b5d63f1a6b824282b57f7b581810d20b7a28ca951f2d5b59f1eb0782c12b/tensorboardx-2.6.4-py3-none-any.whl", hash = "sha256:5970cf3a1f0a6a6e8b180ccf46f3fe832b8a25a70b86e5a237048a7c0beb18e2", size = 87201, upload-time = "2025-06-10T22:37:05.44Z" }, + { url = "https://files.pythonhosted.org/packages/e0/1d/b5d63f1a6b824282b57f7b581810d20b7a28ca951f2d5b59f1eb0782c12b/tensorboardx-2.6.4-py3-none-any.whl", hash = "sha256:5970cf3a1f0a6a6e8b180ccf46f3fe832b8a25a70b86e5a237048a7c0beb18e2", size = 87201 }, ] [[package]] name = "termcolor" version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434 } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, + { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734 }, ] [[package]] @@ -3165,21 +3199,21 @@ version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, ] [[package]] name = "threadpoolctl" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, ] [[package]] @@ -3189,9 +3223,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, ] [[package]] @@ -3201,71 +3235,73 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, - { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, - { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, - { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301 }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308 }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964 }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542 }, ] [[package]] name = "tomli" -version = "2.3.0" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477 }, ] [[package]] name = "torch" -version = "2.9.1" +version = "2.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681, upload-time = "2025-11-12T15:19:56.48Z" }, - { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036, upload-time = "2025-11-12T15:21:01.886Z" }, - { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861, upload-time = "2025-11-12T15:21:30.145Z" }, - { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222, upload-time = "2025-11-12T15:20:46.223Z" }, + { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457 }, + { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962 }, + { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237 }, + { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931 }, + { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450 }, ] [[package]] @@ -3277,49 +3313,49 @@ dependencies = [ { name = "packaging" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144, upload-time = "2023-03-10T22:02:20.455Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162, upload-time = "2023-03-10T22:02:18.818Z" }, + { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162 }, ] [[package]] name = "tornado" version = "6.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632, upload-time = "2025-12-15T19:21:03.836Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909, upload-time = "2025-12-15T19:20:48.382Z" }, - { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163, upload-time = "2025-12-15T19:20:49.791Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746, upload-time = "2025-12-15T19:20:51.491Z" }, - { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083, upload-time = "2025-12-15T19:20:52.778Z" }, - { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315, upload-time = "2025-12-15T19:20:53.996Z" }, - { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003, upload-time = "2025-12-15T19:20:56.101Z" }, - { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412, upload-time = "2025-12-15T19:20:57.398Z" }, - { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392, upload-time = "2025-12-15T19:20:58.692Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481, upload-time = "2025-12-15T19:21:00.008Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886, upload-time = "2025-12-15T19:21:01.287Z" }, - { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910, upload-time = "2025-12-15T19:21:02.571Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909 }, + { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163 }, + { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746 }, + { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083 }, + { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315 }, + { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003 }, + { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412 }, + { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392 }, + { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481 }, + { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886 }, + { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910 }, ] [[package]] name = "tqdm" -version = "4.67.1" +version = "4.67.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "platform_system == 'Windows'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374 }, ] [[package]] name = "traitlets" version = "5.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, ] [[package]] @@ -3330,14 +3366,14 @@ dependencies = [ { name = "torch" }, { name = "transformers", extra = ["sentencepiece", "torch"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754, upload-time = "2025-06-15T13:34:38.522Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073, upload-time = "2025-06-15T13:34:37.468Z" }, + { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073 }, ] [[package]] name = "transformers" -version = "4.57.3" +version = "4.57.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -3351,9 +3387,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680, upload-time = "2025-11-25T15:51:30.139Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463, upload-time = "2025-11-25T15:51:26.493Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498 }, ] [package.optional-dependencies] @@ -3368,34 +3404,35 @@ torch = [ [[package]] name = "triton" -version = "3.5.1" +version = "3.6.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692, upload-time = "2025-11-11T17:40:46.074Z" }, + { url = "https://files.pythonhosted.org/packages/44/ba/b1b04f4b291a3205d95ebd24465de0e5bf010a2df27a4e58a9b5f039d8f2/triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781", size = 175972180 }, + { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201 }, ] [[package]] name = "typer" -version = "0.21.1" +version = "0.24.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "annotated-doc" }, { name = "click" }, { name = "rich" }, { name = "shellingham" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, + { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085 }, ] [[package]] name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, ] [[package]] @@ -3405,59 +3442,59 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, ] [[package]] name = "tzdata" version = "2025.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521 }, ] [[package]] name = "unidecode" version = "1.3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701, upload-time = "2024-01-11T11:58:35.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494, upload-time = "2024-01-11T11:58:33.012Z" }, + { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494 }, ] [[package]] name = "uri-template" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, ] [[package]] name = "urllib3" -version = "2.6.2" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584 }, ] [[package]] name = "uvicorn" -version = "0.40.0" +version = "0.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/ce/eeb58ae4ac36fe09e3842eb02e0eb676bf2c53ae062b98f1b2531673efdd/uvicorn-0.41.0.tar.gz", hash = "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", size = 82633 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783 }, ] [package.optional-dependencies] @@ -3475,19 +3512,19 @@ standard = [ name = "uvloop" version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" }, - { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" }, - { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" }, - { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" }, + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335 }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903 }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499 }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133 }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681 }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261 }, ] [[package]] name = "virtualenv" -version = "20.35.4" +version = "20.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -3495,9 +3532,9 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/54/809199edc537dbace273495ac0884d13df26436e910a5ed4d0ec0a69806b/virtualenv-20.39.0.tar.gz", hash = "sha256:a15f0cebd00d50074fd336a169d53422436a12dfe15149efec7072cfe817df8b", size = 5869141 } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b4/8268da45f26f4fe84f6eae80a6ca1485ffb490a926afecff75fc48f61979/virtualenv-20.39.0-py3-none-any.whl", hash = "sha256:44888bba3775990a152ea1f73f8e5f566d49f11bbd1de61d426fd7732770043e", size = 5839121 }, ] [[package]] @@ -3507,157 +3544,146 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" }, - { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" }, - { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" }, - { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" }, - { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" }, - { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" }, - { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" }, - { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" }, - { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" }, - { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" }, - { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" }, - { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318 }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478 }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894 }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065 }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377 }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837 }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456 }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614 }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690 }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459 }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663 }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453 }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611 }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889 }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616 }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413 }, ] [[package]] name = "wcwidth" -version = "0.2.14" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684 } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189 }, ] [[package]] name = "webcolors" version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, ] [[package]] name = "webencodings" version = "0.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, ] [[package]] name = "websocket-client" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, ] [[package]] name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343 }, + { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021 }, + { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320 }, + { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815 }, + { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054 }, + { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565 }, + { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848 }, + { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249 }, + { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685 }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598 }, ] [[package]] name = "widgetsnbextension" version = "4.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, ] [[package]] name = "wikipedia-api" -version = "0.8.1" +version = "0.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/aa/2e35be124dfc7e581480705f912040172f6570cc12e68a245ba9258c32ef/wikipedia_api-0.8.1.tar.gz", hash = "sha256:b31e93b3f5407c1a1ba413ed7326a05379a3c270df6cf6a211aca67a14c5658b", size = 19934, upload-time = "2025-01-19T23:44:33.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/7b/8b775cf22f266086cefa762b58df0a35a4c4532de3f2f6a0b7432fd31fd1/wikipedia_api-0.9.0.tar.gz", hash = "sha256:6aabd1f9fe34f594f4d36f5af67e468b0a50d713aa9a1d102b4d996bc76dd58e", size = 20041 } [[package]] name = "wrapt" -version = "2.0.1" +version = "2.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/37/ae31f40bec90de2f88d9597d0b5281e23ffe85b893a47ca5d9c05c63a4f6/wrapt-2.1.1.tar.gz", hash = "sha256:5fdcb09bf6db023d88f312bd0767594b414655d58090fc1c46b3414415f67fac", size = 81329 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481, upload-time = "2025-11-07T00:43:11.103Z" }, - { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692, upload-time = "2025-11-07T00:43:13.697Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574, upload-time = "2025-11-07T00:43:14.967Z" }, - { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688, upload-time = "2025-11-07T00:43:18.275Z" }, - { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698, upload-time = "2025-11-07T00:43:19.407Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096, upload-time = "2025-11-07T00:43:16.5Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878, upload-time = "2025-11-07T00:43:20.81Z" }, - { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298, upload-time = "2025-11-07T00:43:22.229Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361, upload-time = "2025-11-07T00:43:24.301Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035, upload-time = "2025-11-07T00:43:28.96Z" }, - { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383, upload-time = "2025-11-07T00:43:25.804Z" }, - { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894, upload-time = "2025-11-07T00:43:27.074Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, + { url = "https://files.pythonhosted.org/packages/ca/21/293b657a27accfbbbb6007ebd78af0efa2083dac83e8f523272ea09b4638/wrapt-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e927375e43fd5a985b27a8992327c22541b6dede1362fc79df337d26e23604f", size = 60554 }, + { url = "https://files.pythonhosted.org/packages/25/e9/96dd77728b54a899d4ce2798d7b1296989ce687ed3c0cb917d6b3154bf5d/wrapt-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c99544b6a7d40ca22195563b6d8bc3986ee8bb82f272f31f0670fe9440c869", size = 61496 }, + { url = "https://files.pythonhosted.org/packages/44/79/4c755b45df6ef30c0dd628ecfaa0c808854be147ca438429da70a162833c/wrapt-2.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b2be3fa5f4efaf16ee7c77d0556abca35f5a18ad4ac06f0ef3904c3399010ce9", size = 113528 }, + { url = "https://files.pythonhosted.org/packages/9f/63/23ce28f7b841217d9a6337a340fbb8d4a7fbd67a89d47f377c8550fa34aa/wrapt-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67c90c1ae6489a6cb1a82058902caa8006706f7b4e8ff766f943e9d2c8e608d0", size = 115536 }, + { url = "https://files.pythonhosted.org/packages/23/7b/5ca8d3b12768670d16c8329e29960eedd56212770365a02a8de8bf73dc01/wrapt-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05c0db35ccffd7480143e62df1e829d101c7b86944ae3be7e4869a7efa621f53", size = 114716 }, + { url = "https://files.pythonhosted.org/packages/c7/3a/9789ccb14a096d30bb847bf3ee137bf682cc9750c2ce155f4c5ae1962abf/wrapt-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0c2ec9f616755b2e1e0bf4d0961f59bb5c2e7a77407e7e2c38ef4f7d2fdde12c", size = 113200 }, + { url = "https://files.pythonhosted.org/packages/cf/e5/4ec3526ce6ce920b267c8d35d2c2f0874d3fad2744c8b7259353f1132baa/wrapt-2.1.1-cp310-cp310-win32.whl", hash = "sha256:203ba6b3f89e410e27dbd30ff7dccaf54dcf30fda0b22aa1b82d560c7f9fe9a1", size = 57876 }, + { url = "https://files.pythonhosted.org/packages/d1/4e/661c7c76ecd85375b2bc03488941a3a1078642af481db24949e2b9de01f4/wrapt-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:6f9426d9cfc2f8732922fc96198052e55c09bb9db3ddaa4323a18e055807410e", size = 60224 }, + { url = "https://files.pythonhosted.org/packages/5f/b7/53c7252d371efada4cb119e72e774fa2c6b3011fc33e3e552cdf48fb9488/wrapt-2.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:69c26f51b67076b40714cff81bdd5826c0b10c077fb6b0678393a6a2f952a5fc", size = 58645 }, + { url = "https://files.pythonhosted.org/packages/c4/da/5a086bf4c22a41995312db104ec2ffeee2cf6accca9faaee5315c790377d/wrapt-2.1.1-py3-none-any.whl", hash = "sha256:3b0f4629eb954394a3d7c7a1c8cca25f0b07cefe6aa8545e862e9778152de5b7", size = 43886 }, ] [[package]] name = "xmltodict" version = "0.14.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942, upload-time = "2024-10-16T06:10:29.683Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981, upload-time = "2024-10-16T06:10:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981 }, ] [[package]] name = "xxhash" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" }, - { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" }, - { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" }, - { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" }, - { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" }, - { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" }, - { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" }, - { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" }, - { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" }, - { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" }, - { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" }, - { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" }, + { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845 }, + { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807 }, + { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786 }, + { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830 }, + { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606 }, + { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872 }, + { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217 }, + { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139 }, + { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669 }, + { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018 }, + { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058 }, + { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628 }, + { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577 }, + { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487 }, + { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863 }, ] [[package]] @@ -3669,23 +3695,23 @@ dependencies = [ { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, - { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, - { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, - { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, - { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, - { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, - { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, - { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, - { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, - { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, - { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, - { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, - { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, - { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, - { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, - { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517 }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495 }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400 }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545 }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598 }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893 }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240 }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965 }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026 }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637 }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082 }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811 }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223 }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118 }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852 }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012 }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814 }, ] From 8f507c8b0daa37c835fdff98d543cc496a20226b Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 2 Mar 2026 18:00:11 +0000 Subject: [PATCH 089/101] =?UTF-8?q?=F0=9F=93=9D=20Reorganize=20and=20updat?= =?UTF-8?q?e=20v1.5.0=20documentation=20(EN/ES)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.es.md | 161 ++ README.md | 165 +- docs/CONTRIBUTING.md | 95 +- docs/README.md | 28 + docs/SECURITY.md | 2 +- docs/anonymization/README.md | 81 - docs/api/README.md | 330 +++- docs/data/en/entities-table.md | 32 - docs/data/es/entities-table.md | 27 - docs/database/README.md | 175 +- docs/database/schema.excalidraw | 56 + docs/database/schema.jpg | Bin 142043 -> 0 bytes docs/database/schema.mmd | 53 + docs/database/schema.png | Bin 0 -> 67305 bytes docs/entities/README.md | 17 + docs/entities/anonymizer/README.md | 45 + docs/entities/datapublic/README.md | 47 + docs/es/README.md | 28 + docs/es/api/README.md | 321 +++ docs/es/database/README.md | 113 ++ docs/es/entities/README.md | 17 + docs/es/entities/anonymizer/README.md | 45 + docs/es/entities/datapublic/README.md | 47 + docs/es/models/README.md | 19 + docs/es/models/anonymizer-model-card.md | 172 ++ docs/es/models/decision-model-card.md | 217 ++ docs/es/models/flair-model-card.md | 166 ++ docs/es/pipelines/README.md | 25 + docs/es/pipelines/anonymizer/README.md | 68 + docs/es/pipelines/datapublic/README.md | 76 + docs/models/README.md | 19 + docs/models/anonymizer-model-card.md | 172 ++ .../assets/ner-schema.png | Bin docs/models/decision-model-card.md | 217 ++ docs/{pipeline => models}/flair-model-card.md | 66 +- docs/pipeline/README.md | 56 - .../assets/decision-confusion-matrix.png | Bin 15037 -> 0 bytes docs/pipeline/assets/decision-schema.png | Bin 189471 -> 0 bytes docs/pipeline/assets/inference-example.png | Bin 80766 -> 0 bytes .../assets/postprocessing-example.png | Bin 64041 -> 0 bytes docs/pipeline/decision-model-card.md | 161 -- docs/pipelines/README.md | 25 + docs/pipelines/anonymizer/README.md | 68 + docs/pipelines/anonymizer/pipeline.excalidraw | 1748 +++++++++++++++++ docs/pipelines/anonymizer/pipeline.png | Bin 0 -> 601943 bytes docs/pipelines/datapublic/README.md | 76 + .../datapublic}/pipeline.png | Bin notebooks/experiments/decision/README.md | 14 - notebooks/experiments/ner/flair/README.md | 16 - .../datapublic}/label-studio-config.xml | 0 tutorials/00-annotations/01-export-docs.ipynb | 147 -- .../00-annotations/02-doc-visualization.ipynb | 137 -- tutorials/00-annotations/README.md | 25 - tutorials/01-pipeline/01-evaluation.ipynb | 169 -- .../02-run-ner-from-huggingface.ipynb | 64 - tutorials/GET_STARTED.md | 4 - 56 files changed, 4658 insertions(+), 1154 deletions(-) create mode 100644 README.es.md create mode 100644 docs/README.md delete mode 100644 docs/anonymization/README.md delete mode 100644 docs/data/en/entities-table.md delete mode 100644 docs/data/es/entities-table.md create mode 100644 docs/database/schema.excalidraw delete mode 100644 docs/database/schema.jpg create mode 100644 docs/database/schema.mmd create mode 100644 docs/database/schema.png create mode 100644 docs/entities/README.md create mode 100644 docs/entities/anonymizer/README.md create mode 100644 docs/entities/datapublic/README.md create mode 100644 docs/es/README.md create mode 100644 docs/es/api/README.md create mode 100644 docs/es/database/README.md create mode 100644 docs/es/entities/README.md create mode 100644 docs/es/entities/anonymizer/README.md create mode 100644 docs/es/entities/datapublic/README.md create mode 100644 docs/es/models/README.md create mode 100644 docs/es/models/anonymizer-model-card.md create mode 100644 docs/es/models/decision-model-card.md create mode 100644 docs/es/models/flair-model-card.md create mode 100644 docs/es/pipelines/README.md create mode 100644 docs/es/pipelines/anonymizer/README.md create mode 100644 docs/es/pipelines/datapublic/README.md create mode 100644 docs/models/README.md create mode 100644 docs/models/anonymizer-model-card.md rename docs/{pipeline => models}/assets/ner-schema.png (100%) create mode 100644 docs/models/decision-model-card.md rename docs/{pipeline => models}/flair-model-card.md (69%) delete mode 100644 docs/pipeline/README.md delete mode 100644 docs/pipeline/assets/decision-confusion-matrix.png delete mode 100644 docs/pipeline/assets/decision-schema.png delete mode 100644 docs/pipeline/assets/inference-example.png delete mode 100644 docs/pipeline/assets/postprocessing-example.png delete mode 100644 docs/pipeline/decision-model-card.md create mode 100644 docs/pipelines/README.md create mode 100644 docs/pipelines/anonymizer/README.md create mode 100644 docs/pipelines/anonymizer/pipeline.excalidraw create mode 100644 docs/pipelines/anonymizer/pipeline.png create mode 100644 docs/pipelines/datapublic/README.md rename docs/{pipeline/assets => pipelines/datapublic}/pipeline.png (100%) delete mode 100644 notebooks/experiments/decision/README.md delete mode 100644 notebooks/experiments/ner/flair/README.md rename {docs/data => resources/annotations/label-studio/datapublic}/label-studio-config.xml (100%) delete mode 100644 tutorials/00-annotations/01-export-docs.ipynb delete mode 100644 tutorials/00-annotations/02-doc-visualization.ipynb delete mode 100644 tutorials/00-annotations/README.md delete mode 100644 tutorials/01-pipeline/01-evaluation.ipynb delete mode 100644 tutorials/01-pipeline/02-run-ner-from-huggingface.ipynb delete mode 100644 tutorials/GET_STARTED.md diff --git a/README.es.md b/README.es.md new file mode 100644 index 00000000..eb8acd07 --- /dev/null +++ b/README.es.md @@ -0,0 +1,161 @@ +# AymurAI Backend +Idioma: [English](README.md) | **Español** + +AymurAI Backend provee la API y los pipelines de ML usados para procesar resoluciones judiciales en dos flujos principales: + +- `anonymizer`: extracción de entidades y generación de documentos anonimizados. +- `data-public`: extracción de información estructurada para curación de dataset público. + +Este repositorio contiene el servicio FastAPI, configuraciones de pipelines de producción y persistencia en base de datos para ambos flujos. + +## Qué es AymurAI +AymurAI es un proyecto orientado a facilitar la generación de datos judiciales anonimizados y estructurados para casos de violencia de género (VG) en América Latina. El backend orquesta la ingesta de documentos, la inferencia de modelos, la persistencia de validaciones y la exportación de resultados para usos operativos y de investigación. + +Este repositorio está enfocado en el backend: expone APIs consumidas por el frontend y ejecuta los pipelines de producción de `anonymizer` y `data-public`. + +## Documentación +- Índice técnico: [docs/es/README.md](docs/es/README.md) +- Referencia de API: [docs/es/api/README.md](docs/es/api/README.md) +- Índice de pipelines: [docs/es/pipelines/README.md](docs/es/pipelines/README.md) +- Flujo anonymizer: [docs/es/pipelines/anonymizer/README.md](docs/es/pipelines/anonymizer/README.md) +- Flujo datapublic: [docs/es/pipelines/datapublic/README.md](docs/es/pipelines/datapublic/README.md) +- Esquema de base de datos interna: [docs/es/database/README.md](docs/es/database/README.md) + +## Inicio Rápido (imagen Docker) +Ejecutar la imagen full de la API (incluye recursos de producción): + +```bash +docker run -d --name aymurai-backend -p 8899:8899 ghcr.io/aymurai/api:full +``` + +Opcional: persistir DB/cache fuera del container (volumen host montado en `/resources/cache`): + +```bash +mkdir -p ./aymurai-cache + +docker run -d --name aymurai-backend -p 8899:8899 \ + -v "$(pwd)/aymurai-cache:/resources/cache" \ + ghcr.io/aymurai/api:full +``` + +Opcional: runtime con GPU (requiere NVIDIA Container Toolkit): + +```bash +docker run -d --name aymurai-backend-gpu --gpus all \ + -e TORCH_DEVICE=cuda \ + -p 8899:8899 \ + ghcr.io/aymurai/api:full +``` + +Abrir Swagger UI: + +```text +http://localhost:8899/docs +``` + +## Inicio Rápido (Docker Compose) +Usar los servicios definidos en `docker-compose.yml`: + +```bash +# CPU, perfil liviano +make api-up + +# CPU, perfil full +make api-full-up + +# GPU, perfil liviano +API_SERVICE=aymurai-api-gpu make api-up + +# GPU, perfil full +API_FULL_SERVICE=aymurai-api-full-gpu make api-full-up +``` + +Ver logs: + +```bash +make api-logs +# o make api-full-logs +``` + +## Resumen de runtime +- Framework: `FastAPI` +- Puerto por defecto: `8899` +- Motor de DB: `SQLModel` + migraciones Alembic al iniciar +- URI de DB por defecto: `sqlite:////resources/cache/sqlite/database.db` +- Configs de pipeline de producción: + - `resources/pipelines/production/flair-anonymizer/pipeline.json` + - `resources/pipelines/production/full-paragraph/pipeline.json` + +## Endpoints públicos principales +- `GET /server/healthcheck` +- `GET /server/stats/summary` +- `POST /misc/document-extract` (y alias deprecado `POST /document-extract`) +- `POST /anonymizer/predict` +- `POST /anonymizer/disambiguate` +- `POST /anonymizer/validation` +- `POST /anonymizer/anonymize-document` +- `POST /datapublic/predict/{document_id}` +- `GET /datapublic/validation/document/{document_id}` +- `POST /datapublic/validation/document/{document_id}` + +Para contratos request/response y ejemplos completos, ver [docs/es/api/README.md](docs/es/api/README.md). + +## Despliegue en red cerrada +Para mover una imagen a un entorno sin internet: + +```bash +docker image save ghcr.io/aymurai/api:full -o aymurai-api-full.tar +docker load -i aymurai-api-full.tar +``` + +## Contribución +Las contribuciones son bienvenidas en documentación, API y mejoras de pipelines. + +- Guía de contribución: [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) +- Seguridad y ética: [docs/SECURITY.md](docs/SECURITY.md) +- Código de conducta: [docs/CODE_OF_CONDUCT.md](docs/CODE_OF_CONDUCT.md) + +## Contribuidores +- **Julián Ansaldo** - [@jansaldo](https://github.com/jansaldo) at [collective.ai](https://collectiveai.io) ([email](mailto:juli@collectiveai.io)) +- **Raúl Barriga** - [@jedzill4](https://github.com/jedzill4) at [collective.ai](https://collectiveai.io) ([email](mailto:r@collectiveai.io)) +- **Sofía del Pozo** - [@sofiadelpozo](https://github.com/sofiadelpozo) at [collective.ai](https://collectiveai.io) ([email](mailto:sofia.delpozo@collectiveai.io)) +- **Paolo Donizetti** - [@padonizetti](https://github.com/padonizetti) at [collective.ai](https://collectiveai.io) ([email](mailto:paolo@collectiveai.io)) +- **Conrado Beatriz** - [@conrabeatriz](https://github.com/conrabeatriz) at [collective.ai](https://collectiveai.io) ([email](mailto:conrado@collectiveai.io)) + +## Citar AymurAI +Si usás AymurAI en investigación o publicaciones, por favor citá: + +```bibtex +@techreport{feldfeber2022, + author = {Feldfeber, Ivana and Quiroga, Yasm\'{\i}n Bel\'{e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} +} +``` + +``` +@inproceedings{10.1145/3706598.3713681, + author = {Ciolfi Felice, Marianela and Feldfeber, Ivana and Glasserman Apicella, Carolina and Quiroga, Yasm\'{\i}n Bel\'{e}n and Ansaldo, Juli\'{a}n and Lapenna, Luciano and Bezchinsky, Santiago and Barriga Rubio, Ra\'{u}l and Garc\'{\i}a, Mail\'{e}n}, + title = {Doing the Feminist Work in AI: Reflections from an AI Project in Latin America}, + booktitle = {Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems}, + series = {CHI '25}, + year = {2025}, + isbn = {9798400713941}, + publisher = {Association for Computing Machinery}, + address = {New York, NY, USA}, + doi = {10.1145/3706598.3713681}, + url = {https://doi.org/10.1145/3706598.3713681}, + abstract = {The contemporary AI development landscape is dominated by big corporations, lacks diversity, and mostly centres the Global North, or applies extractivist logics in the South. This paper showcases a feminist process of AI development from Latin America, where we created an interactive, AI-powered tool that helps criminal court officers open justice data, addressing a data gap on gender-based violence. Through a collaborative autoethnography, drawing from Latin American feminisms, we unpack and visibilize the feminist work that was required, as a crucial step to counter hegemonic narratives. Foregrounding the subjugated knowledges of our experiences, we offer a concrete example of a feminist approach to AI development grounded in practice. With this, we aim to critically inspire those who consider building technology in service of social justice causes, or who choose to build AI systems otherwise.}, + articleno = {998}, + numpages = {18}, + keywords = {Global South, NGO, activism, critical HCI, critical computing, duoethnography, feminist AI, feminist research}, + location = {}, +} +``` + +Para usar la cita más actualizada, consulta la referencia del proyecto en el repositorio de la organización: [github.com/aymurai](https://github.com/aymurai). + +## Licencia +AymurAI es software de código abierto bajo licencia [MIT](LICENSE.md). Esta licencia permite modificar, distribuir y usar de forma privada el software, siempre que se mantenga el crédito correspondiente a la autoría original. diff --git a/README.md b/README.md index d140e5ad..a19c2224 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,162 @@ # AymurAI Backend -This repository contains the backend API and machine learning models for [AymurAI](https://www.aymurai.info), a tool designed to generate anonymized datasets from judicial rulings related to gender-based violence (GBV). +Language: **English** | [Español](README.es.md) -AymurAI's backend is responsible for managing the interaction between the frontend and the machine learning models. It provides an API that handles data input, automates the extraction of information from court rulings, and document edition for anonymization purposes. +AymurAI Backend provides the API and ML pipelines used to process judicial rulings for two main workflows: +- `anonymizer`: extract named entities and produce anonymized documents. +- `data-public`: extract structured information for public dataset curation. -## Table of Contents -* [About AymurAI, its Uses and Limitations](#about-aymurai-its-uses-and-limitations) -* [Deployment](#deployment) -* [Pipeline](#pipeline) -* [Tutorials](#tutorials) -* [Contributing](#contributing) -* [Contributors](#contributors) -* [Citing AymurAI](#citing-aymurai) -* [License](#license) +This repository contains the FastAPI service, production pipeline configs, and database persistence used by both workflows. +## About AymurAI +AymurAI is a project focused on supporting the generation of anonymized and structured judicial data for gender-based violence (GBV) cases in Latin America. The backend service orchestrates document ingestion, ML inference, validation persistence, and document export for downstream operational and research uses. -## About AymurAI, its Uses and Limitations -AymurAI is a tool designed to address the lack of available data in the judicial system regarding gender-based violence (GBV) rulings in Latin America. Its goal is to increase report levels, build trust in the justice system, and improve access to justice for women and LGBTIQ+ people. AymurAI generates and maintains anonymized datasets from legal rulings to better understand GBV and support policy-making, while also contributing to campaigns run by feminist collectives. +This repository is backend-focused: it exposes APIs consumed by the frontend and runs the production pipelines for `anonymizer` and `data-public`. -AymurAI is still a prototype and is currently only implemented in Criminal Court N°10 in the City of Buenos Aires, Argentina. Its capabilities are limited to semi-automated data collection and analysis. The quality, consistency, and availability of the data, as well as cooperation from court officials and the broader cultural and political context, may affect its results. +## Documentation +- Technical docs index: [docs/README.md](docs/README.md) +- API reference: [docs/api/README.md](docs/api/README.md) +- Pipelines index: [docs/pipelines/README.md](docs/pipelines/README.md) +- Anonymizer flow: [docs/pipelines/anonymizer/README.md](docs/pipelines/anonymizer/README.md) +- Datapublic flow: [docs/pipelines/datapublic/README.md](docs/pipelines/datapublic/README.md) +- Internal database schema: [docs/database/README.md](docs/database/README.md) -The models were trained using closed datasets from an Argentine criminal court and are specifically tailored to extract relevant information from GBV-related rulings. The domain-specific training ensures the models' accuracy within this legal and cultural context, though they may not be applicable to other regions with different legal systems or cultural norms. +## Quick Start (Docker Image) +Run the full API image (includes production resources): +```bash +docker run -d --name aymurai-backend -p 8899:8899 ghcr.io/aymurai/api:full +``` -## Deployment -AymurAI's backend is deployed using [Docker](https://www.docker.com/). The Docker images are available at the following registry: +Optional: persist DB/cache outside the container (host volume mounted at `/resources/cache`): ```bash -ghcr.io/aymurai/api:full +mkdir -p ./aymurai-cache + +docker run -d --name aymurai-backend -p 8899:8899 \ + -v "$(pwd)/aymurai-cache:/resources/cache" \ + ghcr.io/aymurai/api:full ``` -### Quick Start -To deploy a production-ready instance of the API, run: +Optional: GPU runtime (requires NVIDIA Container Toolkit): ```bash -docker run -d -p 8899:8899 ghcr.io/aymurai/api:full +docker run -d --name aymurai-backend-gpu --gpus all \ + -e TORCH_DEVICE=cuda \ + -p 8899:8899 \ + ghcr.io/aymurai/api:full ``` -This command will start the API on port `8899` on your local machine. You can access the API documentation through OpenAPI at: +Open Swagger UI: -``` +```text http://localhost:8899/docs ``` -Once it is deployed, it doesn't require an internet connection to work. - -### Running on a Closed Network -If you need to deploy in an environment without internet access, export the Docker image by running: +## Quick Start (Docker Compose) +Use the services defined in `docker-compose.yml`: ```bash -docker image save ghcr.io/aymurai/api:full -o aymurai-api.tar -``` - -Transfer the image to the target machine and load it: +# CPU, lightweight API profile +make api-up -```bash -docker load -i aymurai-api.tar -``` +# CPU, full API profile +make api-full-up -For more information on Docker deployment, refer to the [Docker documentation](https://docs.docker.com/). If you need further assistance, feel free to contact us at [aymurai@datagenero.org](mailto:aymurai@datagenero.org). +# GPU, lightweight API profile +API_SERVICE=aymurai-api-gpu make api-up +# GPU, full API profile +API_FULL_SERVICE=aymurai-api-full-gpu make api-full-up +``` -## Pipeline -AymurAI’s backend utilizes a structured data processing pipeline to handle anonymized legal rulings and extract relevant information. This data is processed and made accessible via the API. For more details, please refer to the [pipeline documentation](docs/pipeline/README.md). +Check logs: +```bash +make api-logs +# or make api-full-logs +``` -## Tutorials -To get started with AymurAI, refer to our [tutorials](tutorials/GET_STARTED.md). These guides provide step-by-step instructions on setting up and using the AymurAI backend, including configuration, example queries, and more. +## Runtime Overview +- Framework: `FastAPI` +- Default API port: `8899` +- DB engine: `SQLModel` + Alembic migrations on startup +- Default DB URI: `sqlite:////resources/cache/sqlite/database.db` +- Production pipeline configs: + - `resources/pipelines/production/flair-anonymizer/pipeline.json` + - `resources/pipelines/production/full-paragraph/pipeline.json` + +## Main Public Endpoints +- `GET /server/healthcheck` +- `GET /server/stats/summary` +- `POST /misc/document-extract` (and deprecated alias `POST /document-extract`) +- `POST /anonymizer/predict` +- `POST /anonymizer/disambiguate` +- `POST /anonymizer/validation` +- `POST /anonymizer/anonymize-document` +- `POST /datapublic/predict/{document_id}` +- `GET /datapublic/validation/document/{document_id}` +- `POST /datapublic/validation/document/{document_id}` + +For full request/response contracts and examples, see [docs/api/README.md](docs/api/README.md). + +## Closed-Network Deployment +To move an image into a closed environment: +```bash +docker image save ghcr.io/aymurai/api:full -o aymurai-api-full.tar +docker load -i aymurai-api-full.tar +``` ## Contributing -Thank you for your interest in contributing to AymurAI! There are many ways to get involved, from improving documentation to enhancing the codebase. To get started, please review our [contributor guidelines](docs/CONTRIBUTING.md) and our [code of conduct](docs/CODE_OF_CONDUCT.md). We welcome contributions in areas such as expanding the API and improving the data processing pipeline. +Contributions are welcome across documentation, API, and pipeline improvements. +- Contributing guide: [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) +- Security and ethics: [docs/SECURITY.md](docs/SECURITY.md) +- Code of conduct: [docs/CODE_OF_CONDUCT.md](docs/CODE_OF_CONDUCT.md) ## Contributors -* **Julián Ansaldo** - [@jansaldo](https://github.com/jansaldo) at [collective.ai](https://collectiveai.io) ([email](mailto:juli@collectiveai.io)) -* **Raúl Barriga** - [@jedzill4](https://github.com/jedzill4) at [collective.ai](https://collectiveai.io) ([email](mailto:r@collectiveai.io)) +- **Julián Ansaldo** - [@jansaldo](https://github.com/jansaldo) at [collective.ai](https://collectiveai.io) ([email](mailto:juli@collectiveai.io)) +- **Raúl Barriga** - [@jedzill4](https://github.com/jedzill4) at [collective.ai](https://collectiveai.io) ([email](mailto:r@collectiveai.io)) +- **Sofía del Pozo** - [@sofiadelpozo](https://github.com/sofiadelpozo) at [collective.ai](https://collectiveai.io) ([email](mailto:sofia.delpozo@collectiveai.io)) +- **Paolo Donizetti** - [@padonizetti](https://github.com/padonizetti) at [collective.ai](https://collectiveai.io) ([email](mailto:paolo@collectiveai.io)) +- **Conrado Beatriz** - [@conrabeatriz](https://github.com/conrabeatriz) at [collective.ai](https://collectiveai.io) ([email](mailto:conrado@collectiveai.io)) ## Citing AymurAI -If you use AymurAI in your research or any publication, please cite the following paper to acknowledge our work: +If you use AymurAI in research or publications, please cite: ```bibtex @techreport{feldfeber2022, - author = "Feldfeber, Ivana and Quiroga, Yasmín Belén and Guevara, Clarissa and Ciolfi Felice, Marianela", - title = "Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico", - institution = "DataGenero", - year = "2022", - url = "https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view" + author = {Feldfeber, Ivana and Quiroga, Yasm\'{\i}n Bel\'{e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} +} +``` + +``` +@inproceedings{10.1145/3706598.3713681, + author = {Ciolfi Felice, Marianela and Feldfeber, Ivana and Glasserman Apicella, Carolina and Quiroga, Yasm\'{\i}n Bel\'{e}n and Ansaldo, Juli\'{a}n and Lapenna, Luciano and Bezchinsky, Santiago and Barriga Rubio, Ra\'{u}l and Garc\'{\i}a, Mail\'{e}n}, + title = {Doing the Feminist Work in AI: Reflections from an AI Project in Latin America}, + booktitle = {Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems}, + series = {CHI '25}, + year = {2025}, + isbn = {9798400713941}, + publisher = {Association for Computing Machinery}, + address = {New York, NY, USA}, + doi = {10.1145/3706598.3713681}, + url = {https://doi.org/10.1145/3706598.3713681}, + abstract = {The contemporary AI development landscape is dominated by big corporations, lacks diversity, and mostly centres the Global North, or applies extractivist logics in the South. This paper showcases a feminist process of AI development from Latin America, where we created an interactive, AI-powered tool that helps criminal court officers open justice data, addressing a data gap on gender-based violence. Through a collaborative autoethnography, drawing from Latin American feminisms, we unpack and visibilize the feminist work that was required, as a crucial step to counter hegemonic narratives. Foregrounding the subjugated knowledges of our experiences, we offer a concrete example of a feminist approach to AI development grounded in practice. With this, we aim to critically inspire those who consider building technology in service of social justice causes, or who choose to build AI systems otherwise.}, + articleno = {998}, + numpages = {18}, + keywords = {Global South, NGO, activism, critical HCI, critical computing, duoethnography, feminist AI, feminist research}, + location = {}, } ``` -Proper citation helps us continue developing AymurAI and supporting the community. +For the most up-to-date citation, please use the project-level reference in the organization repository: [github.com/aymurai](https://github.com/aymurai). ## License -AymurAI is open-source software licensed under the [MIT License](LICENSE.md). This license allows for modification, distribution, and private use, provided that appropriate credit is given to the original authors. +AymurAI is open-source software licensed under the [MIT License](LICENSE.md). This license allows modification, distribution, and private use, provided that appropriate credit is given to the original authors. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 6d7efff6..4c411155 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,66 +1,77 @@ # Contributing to AymurAI -We are happy to accept your contributions to make `AymurAI` better and more awesome! To avoid unnecessary work on either -side, please stick to the following process: +We are happy to accept contributions that help make `AymurAI` more useful, more robust, and easier to maintain. +To avoid unnecessary work on either side, please use the following flow. -1. Check if there is already [an issue](https://github.com/AymurAI/dev/issues) for your concern. -2. If there is not, open a new one to start a discussion. We hate to close finished PRs! -3. If we decide your concern needs code changes, we would be happy to accept a pull request. Please consider the -commit guidelines below. +## Contribution flow +1. Check whether there is already an issue for your topic: +2. If not, open a new issue with context, motivation, and expected outcome. +3. Once the scope is clear, submit a pull request tied to that issue. -In case you just want to help out and don't know where to start, -[issues with "help wanted" label](https://github.com/AymurAI/dev/labels/help%20wanted) are good for -first-time contributors. +If you want to help and do not know where to start, small documentation fixes, test coverage improvements, and cleanup PRs are all welcome. +## Local development +If you want to get deeper into the API, we recommend cloning the repository and running the stack locally. +The codebase is fairly navigable, and most of the important modules are documented or organized by workflow. +### Option A: Docker (recommended) +You can use the provided compose services directly: -## Developing locally +```bash +make api-up +# or make api-full-up +``` -For contributors looking to get deeper into the API we suggest cloning the repository. -Nearly all classes and methods are documented, so finding your way around -the code should hopefully be easy. +Swagger UI: `http://localhost:8899/docs` -### Setup +If you prefer working from VS Code, the repository also includes a `.devcontainer/` setup. -#### Using Docker and devcontainer (recommended) -You can use the provided `devcontainer` load all the tools and packages needed. This can be done directly from Visual Studio Code. -You can check the [devcontainer documentation](https://code.visualstudio.com/docs/remote/containers) for more information. +### Option B: Local Python environment +Repository requires Python `3.10`. -#### Using jupyterlab image -If you want just check the notebooks and tutorials you can use the `jupyterlab` docker image. To run the image in `gpu` mode run: -```bash -make jupyter-run -``` -alternatively you can run in `cpu` mode with: ```bash -make jupyter-run-cpu -``` - +# if using uv +uv sync --all-groups -#### Install direclty on a your python environment -create a python environment of your preference and run: -```bash -pip install src/aymurai +# fallback with pip +pip install -e . ``` -You may need to install redis or run it in a docker container. You can use the following command to run it in a docker container: -```bash -make redis-run -``` +For most contributors, Docker is the easiest way to get a working API with the expected runtime dependencies. + +## Pre-commit hooks +After installing dependencies, enable the hooks: -### Git pre-commit Hooks -After installing the dependencies, install `pre-commit` hooks via: ```bash pre-commit install ``` -This will automatically run code formatters black and isort for each git commit. Also it will clear all outputs from the notebooks. If you want to more information about why we do this, please refer to the [data security](docs/DATA_SECURITY.md) section. +Configured hooks currently include: +- `black` +- `nbstripout` +This helps keep code formatting consistent and prevents notebook output from leaking into commits. -### Code Formatting +## Formatting +If needed, you can run the formatter manually before committing: -To ensure a standardized code style we use the formatter [black](https://github.com/ambv/black) and for standardizing imports we use [isort](https://github.com/PyCQA/isort). -If your code is not formatted properly, the tests will fail. +```bash +black aymurai/ +``` -If you set up pre-commit hooks, every git commit will automatically run these formatters. Otherwise you can also manually run them, or let your IDE run them on every file save. -Running from the command line works via `black src/aymurai/ && isort src/aymurai/` in the repository root folder. +## Documentation policy +When behavior changes in API, pipelines, or DB persistence, update the corresponding docs in the same PR: +- `README.md` +- `docs/api/README.md` +- `docs/pipelines/README.md` +- `docs/pipelines/anonymizer/README.md` +- `docs/pipelines/datapublic/README.md` +- `docs/database/README.md` +- `docs/entities/README.md` +- `docs/models/README.md` + +If the change is user-facing, documentation should land together with the code. + +## Community and security +- Code of conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) +- Security and ethics: [SECURITY.md](SECURITY.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..5a05e53e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,28 @@ +# Documentation Index +Language: **English** | [Español](es/README.md) + +This index covers operational documentation for `v1.5.0`. + +## Core docs +- Repository entrypoint: [../README.md](../README.md) +- API reference: [api/README.md](api/README.md) +- Pipelines index: [pipelines/README.md](pipelines/README.md) +- Anonymizer flow: [pipelines/anonymizer/README.md](pipelines/anonymizer/README.md) +- Datapublic flow: [pipelines/datapublic/README.md](pipelines/datapublic/README.md) +- Internal database schema: [database/README.md](database/README.md) + +## Entities +- Entities index: [entities/README.md](entities/README.md) +- Datapublic entities: [entities/datapublic/README.md](entities/datapublic/README.md) +- Anonymizer entities: [entities/anonymizer/README.md](entities/anonymizer/README.md) + +## Models +- Models index: [models/README.md](models/README.md) +- Anonymizer NER model card: [models/anonymizer-model-card.md](models/anonymizer-model-card.md) +- Flair NER model card: [models/flair-model-card.md](models/flair-model-card.md) +- Decision model card: [models/decision-model-card.md](models/decision-model-card.md) + +## Governance +- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md) +- Security and ethics: [SECURITY.md](SECURITY.md) +- Code of conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 7e95a465..72aa9d8a 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -11,4 +11,4 @@ AymurAI is guided by a strong ethical position that considers the complexities o We acknowledge that every line of code that we write may potentially contain security issues. We are trying to deal with it responsibly and provide patches as quickly as possible. ## Reporting a Vulnerability -If you have found a security vulnerability, please open a new [issue](https://github.com/AymurAI/dev/issues/new) or report it to us by sending an [email](aymurai@datagenero.org). We will respond to you as soon as possible. \ No newline at end of file +If you have found a security vulnerability, please open a new [issue](https://github.com/AymurAI/backend/issues/new) or report it to us by sending an [email](mailto:aymurai@datagenero.org). We will respond to you as soon as possible. diff --git a/docs/anonymization/README.md b/docs/anonymization/README.md deleted file mode 100644 index 29deee42..00000000 --- a/docs/anonymization/README.md +++ /dev/null @@ -1,81 +0,0 @@ -## Pipeline actual de anonimización - -1. `/misc/document-extract` recibe el DOCX/PDF y devuelve el payload estructurado con `document_id` y la lista de párrafos. -2. `/anonymizer/predict` procesa cada párrafo y retorna las entidades detectadas (`aymurai_label`, texto y offsets) que el frontend reúne sobre el texto completo. Las predicciones originales se persisten en la base de datos. -3. Se validan manualmente las etiquetas consolidadas directamente en la interfaz gráfica. Al confirmar, se persisten las validaciones en la base de datos. -4. `/anonymizer/anonymize-document` aplica los reemplazos según las entidades confirmadas y genera el documento anonimizado listo para descarga. - -## Anonimización con Desambiguación de Entidades Canónicas - -1. `/misc/document-extract` recibe el DOCX/PDF y devuelve el payload estructurado (`document_id`, lista de párrafos) tal como hoy. -2. Se procesa cada párrafo con `/anonymizer/predict`, obteniendo los resultados de NER (`aymurai_label`, texto, offsets, contexto previo y posterior y otros atributos). -3. Módulo de fuzzy-matching cruza cada mención extraída para sugerir candidatos de `CanonicalEntities` (`canonical_text` y `aliases`). -4. Con esa lista se ejecuta el mapeo de `Entity` a `CanonicalEntity`: cada mención NER queda anotada con un `canonical_entity_id` y un `aymurai_label_instance` (índice entero por orden de aparición) y cualquier atributo inferido, generando la desambiguación de entidades. -5. El frontend consume esos outputs y muestra el texto original con dos niveles de revisión: etiquetas NER y asignación canónica. -6. Tras la validación manual, el cliente invoca `/anonymizer/anonymize-document`, que ahora utiliza políticas de render (`render_policy`) para aplicar reemplazos consistentes (etiqueta o subclase con sufijos opcionales) y generar el documento anonimizado final. -7. El documento anonimizado queda disponible para descarga y se registran en la base de datos las decisiones finales (etiquetas y canónicos). - -## Cambios principales en el modelo de datos - -### Nuevos campos en `EntityAttributes` - -- `canonical_entity_id`: UUID de la entidad canónica asignada a cada mención. -- `aymurai_label_instance`: índice entero por orden de aparición del `canonical_entity_id` dentro de un mismo label (1, 2, 3...). -- `aymurai_label_subclass`: en el caso de fechas donde encuentra el patrón se escribe la fecha en formato DD/MM/YYYY. -- `aymurai_disambiguation`: método aplicado para la desambiguación efectiva del label (`fuzzy`, `none`). -- `aymurai_anonymize`: flag efectivo de anonimización (True/False). - -### Políticas por label (nueva interfaz) - -Se incorpora un esquema de políticas por etiqueta que permite decidir: - -- `anonymize`: `true | false` -- `disambiguation`: `"none" | "fuzzy"` -- `use_subclass_when_available`: `true | false` - -Estas políticas pueden venir de: - -1. Configuración del servidor (variable de entorno `DISAMBIGUATION_LABEL_POLICIES`). -2. Request del usuario (campo `label_policies` en `/anonymizer/disambiguate` y `/anonymizer/anonymize-document`). - -## Render Policy (integración con frontend) - -Para evitar sumar flags adicionales en cada entidad, el comportamiento de render se controla con un objeto `render_policy` (a nivel request/documento), que define: - -- `suffix_mode`: `"auto" | "always" | "never"`. -- `suffix_threshold`: umbral para agregar sufijo en modo `auto`. - -Esto permite generar tokens como: - -- `` si hay un solo juez. -- ``, `` si hay múltiples. -- `` si `use_subclass_when_available` es `false` ó `use_subclass_when_available` es `true` pero no hay subclase disponible para esa entidad. - -El `render_policy` puede definirse por entorno (`RENDER_POLICY`) o por request y se aplica al momento de exportar el documento con `/anonymizer/anonymize-document`. - -## Cambios en la API - -### `/anonymizer/disambiguate` - -- **Nuevo input opcional**: `label_policies`. -- **Nuevo output**: `label_policies` y metadatos efectivos en cada `DocLabel` (`aymurai_disambiguation`, `aymurai_anonymize`). -- La selección de labels para fuzzy se define por políticas, no por un modo global. - -### `/anonymizer/anonymize-document` - -- **Input**: `DocumentAnnotations` incluye `label_policies` y `render_policy`. -- **Render**: los tokens se generan según `render_policy` y respetan `aymurai_anonymize`. - -## Plan de evaluación incremental - -El objetivo general es optimizar la extracción y desambiguación de entidades. Para avanzar con criterio orientado a métricas mediremos cada capa y el sistema end-to-end. - -- **NER**: aunque el modelo actual cubre el caso de uso, debemos planificar un nuevo ciclo de entrenamiento y finetuning. Será importante consolidar un dataset público anotado con las entidades a detectar, para lo cual podemos aprovechar repositorios públicos de documentos legales de Argentina (e idealmente, del resto de latinoamérica). Luego, se deberán realizar algunas revisiones manuales para asegurar calidad. Este dataset nos permitirá definir un split de evaluación estable y automatizar el cálculo de F1 por etiqueta para comparar versiones de forma consistente. -- **Heurística de agrupación**: vamos a contrastar variantes como Levenshtein normalizada, token fuzzy matching o TF-IDF con coseno. Cada método se ejecutará sobre predicciones reales del NER (para medir robustez ante ruido) y sobre etiquetas existentes (para estimar el techo teórico). La métrica utilizada para evaluar será aquella definida en `/notebooks/experiments/entity-disambiguation/Desarrollo metrica.md`, contra un conjunto de `CanonicalEntities` ya anotados. - -Finalmente consolidaremos una evaluación integrada combinando el mejor NER disponible, la heurística con mayor cobertura y baja tasa de falsos positivos y la versión de prompt más precisa. El benchmark se ejecutará sobre un conjunto de documentos de validación con etiquetas canónicas revisadas. - -## Consideraciones de integración con frontend - -- Necesitamos habilitar una lista configurable de entidades a excluir de la anonimización. El frontend deberá permitir que la persona usuaria decida, por ejemplo, mantener visibles o no entidades específicas o menciones a funcionarios públicos según su rol procesal. -- Las exclusiones y la edición manual posterior impactan directamente en el ordenamiento de los `aymurai_label_instance`. Habrá que recalcular los sufijos luego de aplicar exclusiones y validaciones para evitar huecos o inconsistencias entre el texto mostrado y los reemplazos finales. diff --git a/docs/api/README.md b/docs/api/README.md index f5d1b028..dd09f428 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -1,35 +1,321 @@ -# API -The connection between AymurAI's frontend and backend occurs through an Application Programming Interface (API). There are several endpoints that allow communication between the different parts of the software. Calls to these endpoints are made from the frontend to execute different subroutines. +# API Reference +Language: **English** | [Español](../es/api/README.md) -# Endpoints -* `/document-extract`: plain text extraction from .doc or .docx documents -* `/predict`: prediction over a single paragraph -* `/predict-batch`: run prediction over a batch of paragraphs +This document describes the currently mounted public API in `aymurai/api/main.py` + `aymurai/api/core.py`. + +## Base URL and OpenAPI +- Local base URL: `http://localhost:8899` +- Swagger UI: `http://localhost:8899/docs` +- OpenAPI JSON: `http://localhost:8899/openapi.json` + +## Public Endpoints (Mounted) + +| Method | Path | Purpose | +|---|---|---| +| `GET` | `/server/healthcheck` | Service liveness check | +| `GET` | `/server/stats/summary` | Runtime CPU/memory stats | +| `POST` | `/document-extract` | Deprecated alias of `/misc/document-extract` | +| `POST` | `/misc/document-extract` | Extract normalized paragraphs from uploaded document | +| `POST` | `/anonymizer/predict` | NER prediction for a paragraph | +| `POST` | `/anonymizer/disambiguate` | Canonical entity disambiguation + policy merge | +| `POST` | `/anonymizer/validation` | Fetch paragraph-level manual validation | +| `POST` | `/anonymizer/anonymize-document` | Compile and export anonymized document | +| `POST` | `/datapublic/predict/{document_id}` | Predict entities for data-public flow | +| `GET` | `/datapublic/validation/document/{document_id}` | Read document-level validation | +| `POST` | `/datapublic/validation/document/{document_id}` | Save document-level validation | +| `POST` | `/convert/pdf/odt` | Convert PDF to ODT | +| `POST` | `/convert/pdf/docx` | Convert PDF to DOCX | +| `POST` | `/convert/docx/odt` | Convert DOCX to ODT | +| `POST` | `/convert/docx/pdf` | Convert DOCX to PDF | +| `POST` | `/convert/odt/pdf` | Convert ODT to PDF | +| `POST` | `/convert/odt/docx` | Convert ODT to DOCX | + +## Core Data Contracts + +Note: the JSON snippets below are minimal valid examples. Real payloads may include additional fields depending on the endpoint and processing stage. + +### `TextRequest` +```json +{ + "text": "Acusado: Ramiro Marrón DNI 34.555.666." +} +``` + +### `EntityAttributes` (relevant fields) +```json +{ + "aymurai_label": "PER", + "aymurai_label_subclass": [], + "aymurai_method": "flair", + "aymurai_score": 0.97, + "canonical_entity_id": "a3f8b60f-8e7e-4c8b-9de2-ec8dd3f44c12", + "aymurai_label_instance": 1, + "aymurai_disambiguation": "fuzzy", + "aymurai_anonymize": true +} +``` + +### `DocLabel` +```json +{ + "text": "Ramiro Marrón", + "start_char": 9, + "end_char": 22, + "attrs": { + "aymurai_label": "PER" + } +} +``` + +### `DocumentInformation` +```json +{ + "document": "Acusado: Ramiro Marrón DNI 34.555.666.", + "labels": [ + { + "text": "Ramiro Marrón", + "start_char": 9, + "end_char": 22, + "attrs": { + "aymurai_label": "PER" + } + } + ] +} +``` + +### `LabelPolicy` +```json +{ + "anonymize": true, + "disambiguation": "fuzzy", + "use_subclass_when_available": true +} +``` + +### `RenderPolicy` +```json +{ + "suffix_mode": "auto", + "suffix_threshold": 1 +} +``` + +### `DocumentAnnotations` +```json +{ + "data": [ + { + "document": "...", + "labels": [] + } + ], + "label_policies": { + "PER": { + "anonymize": true, + "disambiguation": "fuzzy", + "use_subclass_when_available": false + } + }, + "render_policy": { + "suffix_mode": "auto", + "suffix_threshold": 1 + } +} +``` + +## Endpoint Details and Examples + +### Server + +#### `GET /server/healthcheck` +- Response `200`: + +```json +{"status": "ok"} +``` -# Deployment -This project is deployed using [Docker](https://www.docker.com/) , and the images are available in the following registry: ```bash -registry.gitlab.com/collective.ai/datagenero-public/aymurai-api:prod +curl -s http://localhost:8899/server/healthcheck ``` -You can a production ready instance of the API by running: + +#### `GET /server/stats/summary` +- Response `200` (shape): + +```json +{ + "is_docker": true, + "cpu_core_limit": 4, + "cpu_usage_percent": 0.0, + "memory_limit_mb": 4096.0, + "memory_usage_mb": 823.5 +} +``` + ```bash -docker run -d --rm -p 8899:8899 \ - registry.gitlab.com/collective.ai/datagenero-public/aymurai-api:prod +curl -s http://localhost:8899/server/stats/summary ``` -this will start a container with the API running on port 8899 on your localhost. The API is documented using OpenAPI and can be accessed at `http://localhost:8899/docs` -Once it is deployed, it doesn't requere a internet connection to work. If you need to deployed in a closed network you can port the image using + +### Document Extraction + +#### `POST /misc/document-extract` +#### `POST /document-extract` (deprecated alias) +- Request: `multipart/form-data` with `file` +- Supported MIME types in extraction flow: DOCX, ODT, PDF +- Response `200`: + +```json +{ + "document": ["Paragraph 1", "Paragraph 2"], + "document_id": "f2b25507-cf88-5b11-8f2a-c0b6f940b7f8" +} +``` + ```bash -docker image save registry.gitlab.com/collective.ai/datagenero-public/aymurai-api:prod -o aymurai-api.tar +curl -s -X POST \ + -F "file=@test/api/test_file.docx" \ + http://localhost:8899/misc/document-extract ``` -and then transfer the image to the target machine and load it using + +Common errors: +- `504` extraction timeout +- `500` extractor/internal errors + +### Anonymizer + +#### `POST /anonymizer/predict` +- Request body: `TextRequest` +- Query param: `use_cache=true|false` (default `true`) +- Response `200`: `DocumentInformation` + ```bash -docker load -i aymurai-api.tar +curl -s -X POST "http://localhost:8899/anonymizer/predict?use_cache=true" \ + -H "Content-Type: application/json" \ + -d '{"text":"Acusado: Ramiro Marrón DNI 34.555.666."}' ``` -For more information about docker deployment please refer to the [docker documentation](https://docs.docker.com/). -You also can contact us at aymurai@datagenero.org and we will be happy to help you. +#### `POST /anonymizer/disambiguate` +- Request body: + +```json +{ + "paragraphs": [ + { + "document": "Acusado: Ramiro Marrón DNI 34.555.666.", + "labels": [] + } + ], + "label_policies": { + "PER": { + "anonymize": true, + "disambiguation": "fuzzy", + "use_subclass_when_available": false + } + } +} +``` + +- Response `200`: `DocumentAnnotations` (with `data` and effective `label_policies`) + +```bash +curl -s -X POST http://localhost:8899/anonymizer/disambiguate \ + -H "Content-Type: application/json" \ + -d '{"paragraphs":[{"document":"Acusado: Ramiro Marrón DNI 34.555.666.","labels":[]}],"label_policies":{"PER":{"anonymize":true,"disambiguation":"fuzzy"}}}' +``` + +#### `POST /anonymizer/validation` +- Request body: `TextRequest` +- Response `200`: `list[DocLabel] | null` + +```bash +curl -s -X POST http://localhost:8899/anonymizer/validation \ + -H "Content-Type: application/json" \ + -d '{"text":"Acusado: Ramiro Marrón DNI 34.555.666."}' +``` + +#### `POST /anonymizer/anonymize-document` +- Request: `multipart/form-data` + - `file`: original document (`.docx`, `.pdf`, `.odt`) + - `annotations`: JSON string serialized from `DocumentAnnotations` +- Response `200`: binary anonymized `.odt` file + +```bash +curl -X POST http://localhost:8899/anonymizer/anonymize-document \ + -F "file=@test/api/test_file.docx" \ + -F 'annotations={"data":[{"document":"Acusado: Ramiro Marrón DNI 34.555.666.","labels":[]}],"label_policies":{"PER":{"anonymize":true,"disambiguation":"fuzzy"}},"render_policy":{"suffix_mode":"auto","suffix_threshold":1}}' +``` + +Common errors: +- `400` invalid form payload +- `500` conversion/anonymization failures + +### Data-Public + +#### `POST /datapublic/predict/{document_id}` +- Path param: `document_id` (`UUID5`) +- Request body: `TextRequest` +- Query param: `use_cache=true|false` (default `true`) +- Response `200`: `DocumentInformation` + +```bash +curl -s -X POST "http://localhost:8899/datapublic/predict/7e6b6f35-2f29-58f7-9f8e-fd1d9026a6bc?use_cache=true" \ + -H "Content-Type: application/json" \ + -d '{"text":"Buenos Aires, 17 de noviembre de 2024"}' +``` + +#### `GET /datapublic/validation/document/{document_id}` +- Response `200`: object or `null` +- Response `404`: document not found + +```bash +curl -s http://localhost:8899/datapublic/validation/document/7e6b6f35-2f29-58f7-9f8e-fd1d9026a6bc +``` + +#### `POST /datapublic/validation/document/{document_id}` +- Request body: free-form JSON object (stored as document-level validation) +- Response `200`: empty body + +```bash +curl -s -X POST http://localhost:8899/datapublic/validation/document/7e6b6f35-2f29-58f7-9f8e-fd1d9026a6bc \ + -H "Content-Type: application/json" \ + -d '{"materia":"penal","violencia_de_genero":"si"}' +``` + +### Document Conversion + +All conversion endpoints use `multipart/form-data` with a `file` field. + +| Method | Path | Input | Output | +|---|---|---|---| +| `POST` | `/convert/pdf/odt` | `.pdf` | `.odt` | +| `POST` | `/convert/pdf/docx` | `.pdf` | `.docx` | +| `POST` | `/convert/docx/odt` | `.docx` | `.odt` | +| `POST` | `/convert/docx/pdf` | `.docx` | `.pdf` | +| `POST` | `/convert/odt/pdf` | `.odt` | `.pdf` | +| `POST` | `/convert/odt/docx` | `.odt` | `.docx` | + +For PDF input endpoints, optional query param: +- `backend=libreoffice|pandoc` (default: `libreoffice`) + +Example: + +```bash +curl -X POST "http://localhost:8899/convert/pdf/docx?backend=libreoffice" \ + -F "file=@input.pdf" -o output.docx +``` + +Common errors: +- `400` unsupported input extension +- `500` conversion tool failure + +## Legacy / Not Public (Not Mounted) +The following route modules exist in code but are not included in `core.router` at runtime: -# Source -The api source code is written in top of FastAPI and can be found in [here](../../api/). +- `aymurai/api/endpoints/routers/datapublic/dataset.py` + - includes `/datapublic/dataset/*` CRUD/batch routes, but router is not mounted. +- `aymurai/api/endpoints/routers/anonymizer/database.py` + - `/anonymizer/database/*` routes exist, include is commented out. +- `aymurai/api/endpoints/routers/database/*` + - additional DB admin routes exist, but no mounting in `core.router`. -Please follow the [code of conduct](../CODE_OF_CONDUCT.md) and [contributing guidelines](../CONTRIBUTING.md). \ No newline at end of file +Treat these as legacy/internal code paths until explicitly exposed in the public router. diff --git a/docs/data/en/entities-table.md b/docs/data/en/entities-table.md deleted file mode 100644 index 1037b24d..00000000 --- a/docs/data/en/entities-table.md +++ /dev/null @@ -1,32 +0,0 @@ -# Entities -| Entity | Description | -|-----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ART_INFRINGIDO | Article(s) of the infraction(s) in the case. | -| CONDUCTA | Indicates the action relating to the crime, contravention, or misdemeanor described in the article infringed. | -| CONDUCTA_DESCRIPCION | Indicates that the contravention or offence infringed has some particularity, such as being aggravated by some causality. | -| DECISION | Fragment of text denoting a decision taken by the court regarding the case. | -| DETALLE | Specifies OBJETO_DE_LA_RESOLUCION as to what was resolved. | -| EDAD_AL_MOMENTO_DEL_HECHO | Indicates the age of the person at the time of the event. | -| FECHA_DE_NACIMIENTO | Date of birth. | -| FECHA_DEL_HECHO | Date on which the reported event occurred. | -| FECHA_RESOLUCION | Day of resolution. In the case of oral hearings, it is the day of the beginning of the hearing. | -| FRASES_AGRESION | Transcription of the phrases described by the victim as the verbal aggression suffered and that are part of the facts of the case. Applies to cases of verbal violence. | -| GENERO | Gender. | -| HIJOS_HIJAS_EN_COMUN | Indicates whether the accused person and the complainant have children in common. | -| HORA_DE_CIERRE | Time of end of court hearing. | -| HORA_DE_INICIO | Court hearing start time. | -| LUGAR_DEL_HECHO | Indicates the physical place (environment or public road) where the facts occurred or if it was committed by technological means. | -| MODALIDAD_DE_LA_VIOLENCIA | The way in which the different types of violence manifest themselves. For example: domestic, institutional, media, labor, reproductive freedom, obstetric, in public or private spaces, and political and public violence. | -| N_EXPTE_EJE | Case identifier number. | -| NACIONALIDAD | Nationality. | -| NIVEL_INSTRUCCION | Level of formal education attained by the individual. | -| NOMBRE | Name. | -| OBJETO_DE_LA_RESOLUCION | About what was resolved. | -| PERSONA_ACUSADA_NO_DETERMINADA | In case the accused person is not a natural person or is not determined. For example: legal entity, social network user, etc. | -| RELACION_Y_TIPO_ENTRE_ACUSADO/A_Y_DENUNCIANTE | Indicates the type of relationship the accused person has with the complainant. | -| TIPO_DE_RESOLUCION | Interlocutory decisions are those that define a specific issue during the process of the proceeding, or final decisions are those that put an end to the process of the case. | -| VIOLENCIA_DE_GENERO | If the fact under investigation is within a context of gender violence. | -# Subcategories -The majority of the entities described above have subcategories acording their use in the criminal court and the type of information they contain. You can check a more complete description [here](https://docs.google.com/document/d/123B9T2abCEqBaxxOl5c7HBJZRdIMtKDWo6IKHIVil04/edit) (Spanish version) - -A comprehensive list of all entities and their subcategory can be found [here](https://docs.google.com/spreadsheets/d/1FTRtltYJrGJ6huQd0ih-QBZfqo5LGamVFXvXKcmX9-k/edit#gid=0) \ No newline at end of file diff --git a/docs/data/es/entities-table.md b/docs/data/es/entities-table.md deleted file mode 100644 index 1f4b9301..00000000 --- a/docs/data/es/entities-table.md +++ /dev/null @@ -1,27 +0,0 @@ -| Entidad | Descripcion | -|-----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ART_INFRINGIDO | Artículo/s de la/s infracción/es en el caso | -| CONDUCTA | Indica la acción relativa al delito, la contravención, o la falta que aparece descripta en el artículo infringido; | -| CONDUCTA_DESCRIPCION | Indica la contravención o la falta infringida tiene alguna particularidad, como puede ser que se encuentra agravada por alguna causal | -| DECISION | Fragmento de texto que denota una decision tomada por el juzgado respecto al caso. | -| DETALLE | Especifica OBJETO_DE_LA_RESOLUCIÓN respecto a qué se resolvió | -| EDAD_AL_MOMENTO_DEL_HECHO | Indica la edad de la persona al momento del hecho | -| FECHA_DE_NACIMIENTO | Fecha de nacimiento | -| FECHA_DEL_HECHO | Fecha en que sucedio el hecho denunciado | -| FECHA_RESOLUCION | Día de la resolución. En caso de las audiencias orales es el día de su inicio | -| FRASES_AGRESION | Transcripción de las frases descriptas por la víctima como la agresión verbal sufrida y que son parte de los hechos del caso. Aplica para los casos de violencia verbal; | -| GENERO | Genero | -| HIJOS_HIJAS_EN_COMUN | indica si la persona acusada y la denunciante tienen hijos/as en común | -| HORA_DE_CIERRE | Hora de finalización de la audiencia | -| HORA_DE_INICIO | Hora de inicio de la audiencia | -| LUGAR_DEL_HECHO | Indica lugar físico (ambiente o vía pública) donde ocurrieron los hechos o si fue cometido mediante medios tecnológicos | -| MODALIDAD_DE_LA_VIOLENCIA | Forma en que se manifiestan los distintos tipos de violencia | -| N_EXPTE_EJE | Numeroo identificador del caso | -| NACIONALIDAD | Nacioalidad | -| NIVEL_INSTRUCCION | Nivel de estudios formales alcanzados por la persona | -| NOMBRE | Nombre | -| OBJETO_DE_LA_RESOLUCION | Sobre qué se resolvió | -| PERSONA_ACUSADA_NO_DETERMINADA | En el caso de que la persona acusada no sea una persona física o no esté determinada. Por ejemplo: persona jurídica, usuario de red social, etc. | -| RELACION_Y_TIPO_ENTRE_ACUSADO/A_Y_DENUNCIANTE | Indica el tipo de vínculo que tiene la persona acusada con la denunciante | -| TIPO_DE_RESOLUCION | Interlocutorias son aquellas que definen una cuestión concreta durante la tramitación del proceso, o definitivas son aquellas resoluciones que ponen fin al proceso de la causa | -| VIOLENCIA_DE_GENERO | Si el hecho objeto de investigación se encuentra dentro de un contexto de violencia de género. | diff --git a/docs/database/README.md b/docs/database/README.md index 387ea6ed..64cf5212 100644 --- a/docs/database/README.md +++ b/docs/database/README.md @@ -1,66 +1,113 @@ -# Descripción del nuevo flujo de integración en AymurAI - -En este documento, describimos brevemente el nuevo flujo de integración entre el _front-end_ y el _back-end_ de AymurAI. - -## Actualización principal del back-end - -Se implementó un motor de base de datos que permite persistir en disco tanto las predicciones generadas por los modelos como las validaciones manuales realizadas desde la interfaz gráfica. Esta base de datos no sólo funciona como un caché para las predicciones, sino que además permite a les usuaries recuperar la última validación manual en caso de necesitar reprocesar un documento específico. - -A continuación, se detalla el nuevo flujo para cada _pipeline_ de AymurAI: +# Internal Database +Language: **English** | [Español](../es/database/README.md) + +This document describes the internal persistence used by the API runtime. + +## Engine and Migrations +- ORM: `SQLModel` (`sqlalchemy` backend) +- Migration tool: Alembic +- Runtime setting: `SQLALCHEMY_DATABASE_URI` +- Default DB URI: `sqlite:////resources/cache/sqlite/database.db` +- Startup behavior: + 1. API checks DB connectivity. + 2. If SQLite file does not exist, it creates parent directories. + 3. API runs `alembic upgrade head` on startup. + +Related code: +- `aymurai/settings.py` +- `aymurai/api/startup/database.py` +- `aymurai/api/main.py` +- `aymurai/database/versions/13f78d08e925_create_database.py` + +## ER Diagram +![Database ER diagram](schema.png) + +Editable source: [`schema.mmd`](schema.mmd) + +## Tables + +### `anonymization_document` +| Column | Type | Nullable | Notes | +|---|---|---|---| +| `id` | `UUID` | no | Primary key | +| `created_at` | `DATETIME` | no | Server default `CURRENT_TIMESTAMP` | +| `updated_at` | `DATETIME` | yes | Updated on row changes | +| `name` | `TEXT` | no | Original filename | + +### `anonymization_paragraph` +| Column | Type | Nullable | Notes | +|---|---|---|---| +| `id` | `UUID` | no | Primary key, derived from paragraph text hash | +| `text` | `TEXT` | no | Normalized paragraph text | +| `prediction` | `JSON` | yes | Model predictions (`list[DocLabel]`) | +| `validation` | `JSON` | yes | Manual labels (`list[DocLabel]`) | +| `created_at` | `DATETIME` | no | Server default `CURRENT_TIMESTAMP` | +| `updated_at` | `DATETIME` | yes | Updated on row changes | + +### `anonymization_document_paragraph` +| Column | Type | Nullable | Notes | +|---|---|---|---| +| `id` | `UUID` | no | Link row identifier | +| `document_id` | `UUID` | no | FK -> `anonymization_document.id` | +| `paragraph_id` | `UUID` | no | FK -> `anonymization_paragraph.id` | +| `order` | `INTEGER` | yes | Paragraph order in source document | + +Primary key is composite over `id`, `document_id`, `paragraph_id`. + +### `datapublic_document` +| Column | Type | Nullable | Notes | +|---|---|---|---| +| `id` | `UUID` | no | Primary key (document identifier) | +| `prediction` | `JSON` | yes | Reserved document-level prediction payload; not written by the current public router | +| `validation` | `JSON` | yes | Document-level validation payload | +| `created_at` | `DATETIME` | no | Server default `CURRENT_TIMESTAMP` | +| `updated_at` | `DATETIME` | yes | Updated on row changes | + +### `datapublic_paragraph` +| Column | Type | Nullable | Notes | +|---|---|---|---| +| `id` | `UUID` | no | Primary key, derived from paragraph text hash | +| `text` | `TEXT` | no | Normalized paragraph text | +| `prediction` | `JSON` | yes | Model predictions (`list[DocLabel]`) | +| `validation` | `JSON` | yes | Reserved for paragraph-level validation; public route is currently commented legacy logic | +| `created_at` | `DATETIME` | no | Server default `CURRENT_TIMESTAMP` | +| `updated_at` | `DATETIME` | yes | Updated on row changes | + +### `datapublic_document_paragraph` +| Column | Type | Nullable | Notes | +|---|---|---|---| +| `id` | `UUID` | no | Link row identifier | +| `document_id` | `UUID` | no | FK -> `datapublic_document.id` | +| `paragraph_id` | `UUID` | no | FK -> `datapublic_paragraph.id` | +| `order` | `INTEGER` | yes | Paragraph order in source document | + +Primary key is composite over `id`, `document_id`, `paragraph_id`. + +## Endpoint to Persistence Mapping ### Anonymizer - -1. **Consulta inicial en la base de datos (_endpoint_ `/anonymizer/validation`):** - - Tras la extracción del texto del documento, se verifica si existe una validación guardada para cada uno de sus párrafos. El identificador utilizado es un _hash_ único generado a partir del contenido textual del párrafo. - - - **Si existe una validación previa en la tabla `anonymization_paragraph`:** se retorna dicha validación. - - **Si no existe una validación previa:** debe dispararse el flujo de predicciones consumiendo el _endpoint_ correspondiente. - -2. **Consulta de predicciones (_endpoint_ `/anonymizer/predict`):** - - - **Si la predicción para el párrafo ya existe en la tabla `anonymization_paragraph`:** se retorna. - - **Si no existe:** se ejecuta el modelo de predicción, y los resultados se retornan y se persisten en la tabla `anonymization_paragraph`. - - > **Formato de etiquetas:** tanto las validaciones manuales como las predicciones se estructuran utilizando el formato actual de las etiquetas de AymurAI. - -3. **Interfaz gráfica y validación manual:** - - Las etiquetas retornadas se muestran en la interfaz gráfica, donde pueden ser validadas manualmente. Una vez finalizada la validación, se procede con la anonimización del documento (_endpoint_ `/anonymizer/anonymize-document`). - -4. **Persistencia de validaciones:** - - Al finalizar el proceso, las validaciones se escriben en la tabla `anonymization_paragraph`, ya sea creando nuevos registros para párrafos nuevos o actualizando registros existentes para párrafos previamente procesados. Esta escritura es realizada al inicio del flujo de `anonymize-document`, luego de que se indique desde la UI que el documento está listo para su anonimización. Además, en la tabla `anonymization_document` se escribe la referencia al ID del documento y el nombre del archivo correspondiente. Asimismo, el ID del documento permite recuperar los párrafos y su orden en el documento a través de la tabla `anonymization_document_paragraph`. - -### Data Public - -El flujo para el _pipeline_ de Data Public es similar al del Anonymizer, pero presenta diferencias importantes en cuanto a las validaciones, ya que estas no comparten el mismo formato que las predicciones originales del modelo. En este caso, las validaciones se realizan a nivel general del documento (no por párrafo) y se llevan a cabo manualmente en la sección lateral derecha de la UI, donde se definen los campos que serán volcados al dataset público. A diferencia del _pipeline_ de anonimización, las predicciones siempre son necesarias para resaltar los campos detectados en el texto, incluso cuando ya existen validaciones realizadas, ya que estas no son modificaciones directas sobre las entidades reconocidas por el modelo. - -1. **Consulta inicial en la base de datos (_endpoint_ `/datapublic/dataset/{document_id}`):** - - Tras la extracción del texto del documento, se verifica si existe una validación general para el documento. - - - **Si existe una validación previa para el documento en la tabla `datapublic_dataset`:** se retorna dicha validación. - -2. **Flujo de predicciones (_endpoint_ `/datapublic/predict`):** - - A su vez, se consulta si existen predicciones guardadas para cada uno de los párrafos: - - - **Si existe una predicción previa para cada párrafo en la tabla `datapublic_paragraph`:** se retorna dicha predicción. - - - **Si no existe:** se ejecuta el modelo de predicción, y los resultados se retornan y se persisten en la tabla `datapublic_paragraph`. - - > **Formato de etiquetas:** las predicciones se estructuran utilizando el formato actual de las etiquetas de AymurAI. Las validaciones, sin embargo, tienen un formato distinto, **equivalente al de las filas del dataset público**. Es necesario tener presente esto para poder integrar correctamente con el _front-end_ los campos validados persistidos en la base de datos. - -3. **Interfaz gráfica y validación manual:** - - Una vez finalizadas las consultas a la base de datos y el flujo de predicciones, las etiquetas retornadas se muestran en la interfaz gráfica, donde pueden ser revisadas y validadas manualmente. Posteriormente, las nuevas validaciones se persisten en la tabla `datapublic_dataset` haciendo un _post_ contra el _endpoint_ `/datapublic/dataset`, ya sea creando nuevos registros para documentos nuevos o actualizando registros existentes para documentos previamente procesados. Esta escritura debe realizarse una vez que desde la UI se confirma que los datos extraídos del documento son correctos. - - -#### Requerimientos a la UI - -Para la correcta persistencia de los datos (por ejemplo, para hacer evaluaciones o futuros entrenamientos de los modelos), se necesita que la UI envíe los datos corregidos a la API. Esto puede ser resumido en un _endpoint_ de "compilación" al final del proceso de validación. Este _endpoint_ sería equivalente al `anonymization/anonymize-document` - -## Esquema de la base de datos - -![schema](schema.jpg) +- `POST /anonymizer/predict` + - Reads `anonymization_paragraph` by paragraph UUID. + - Writes `anonymization_paragraph.prediction` when cache is enabled. +- `POST /anonymizer/disambiguate` + - Writes disambiguated predictions to `anonymization_paragraph.prediction`. +- `POST /anonymizer/validation` + - Reads `anonymization_paragraph.validation`. +- `POST /anonymizer/anonymize-document` + - Writes `anonymization_paragraph.validation`. + - Creates `anonymization_document` keyed by uploaded file content hash. + - Creates link rows in `anonymization_document_paragraph`. + +### Data-public +- `POST /datapublic/predict/{document_id}` + - Uses caller-provided `document_id` as the document primary key. + - Ensures `datapublic_document` exists when `use_cache=true`. + - Writes `datapublic_paragraph.prediction` when `use_cache=true`. + - Writes link row in `datapublic_document_paragraph` when `use_cache=true`. +- `GET /datapublic/validation/document/{document_id}` + - Reads `datapublic_document.validation`. +- `POST /datapublic/validation/document/{document_id}` + - Upserts `datapublic_document.validation`. + +## Legacy Note +Route modules for dataset CRUD (`/datapublic/dataset/*`) exist in code but are not mounted in the public router. Do not treat them as active public API until exposed in `core.router`. diff --git a/docs/database/schema.excalidraw b/docs/database/schema.excalidraw new file mode 100644 index 00000000..111dd9fe --- /dev/null +++ b/docs/database/schema.excalidraw @@ -0,0 +1,56 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "UnZYOKsPO14ownJjIhB0K", + "type": "image", + "x": 216.9140625, + "y": 240.5078125, + "width": 968.171875, + "height": 397.984375, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": null, + "seed": 810215012, + "version": 5, + "versionNonce": 845707748, + "isDeleted": false, + "boundElements": [], + "updated": 1772457952595, + "link": null, + "locked": false, + "status": "saved", + "fileId": "X7nK5-PoRG7EVUWSedgsz", + "scale": [ + 1, + 1 + ], + "crop": null + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": { + "X7nK5-PoRG7EVUWSedgsz": { + "id": "X7nK5-PoRG7EVUWSedgsz", + "mimeType": "image/svg+xml", + "dataURL": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGFyaWEtcm9sZWRlc2NyaXB0aW9uPSJlciIgcm9sZT0iZ3JhcGhpY3MtZG9jdW1lbnQgZG9jdW1lbnQiIHZpZXdCb3g9IjAgMCA5NjguMTgxNzAxNjYwMTU2MiAzOTgiIHN0eWxlPSJtYXgtd2lkdGg6IDk2OC4xODE3MDE2NjAxNTYycHg7IiB3aWR0aD0iOTY4LjE3MTg3NSIgaWQ9Im1lcm1haWQtdG8tZXhjYWxpZHJhdyIgaGVpZ2h0PSIzOTcuOTg0Mzc1Ij48c3R5bGU+I21lcm1haWQtdG8tZXhjYWxpZHJhd3tmb250LWZhbWlseToidHJlYnVjaGV0IG1zIix2ZXJkYW5hLGFyaWFsLHNhbnMtc2VyaWY7Zm9udC1zaXplOjI1cHg7ZmlsbDojMzMzO30jbWVybWFpZC10by1leGNhbGlkcmF3IC5lcnJvci1pY29ue2ZpbGw6IzU1MjIyMjt9I21lcm1haWQtdG8tZXhjYWxpZHJhdyAuZXJyb3ItdGV4dHtmaWxsOiM1NTIyMjI7c3Ryb2tlOiM1NTIyMjI7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLmVkZ2UtdGhpY2tuZXNzLW5vcm1hbHtzdHJva2Utd2lkdGg6MnB4O30jbWVybWFpZC10by1leGNhbGlkcmF3IC5lZGdlLXRoaWNrbmVzcy10aGlja3tzdHJva2Utd2lkdGg6My41cHg7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLmVkZ2UtcGF0dGVybi1zb2xpZHtzdHJva2UtZGFzaGFycmF5OjA7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLmVkZ2UtcGF0dGVybi1kYXNoZWR7c3Ryb2tlLWRhc2hhcnJheTozO30jbWVybWFpZC10by1leGNhbGlkcmF3IC5lZGdlLXBhdHRlcm4tZG90dGVke3N0cm9rZS1kYXNoYXJyYXk6Mjt9I21lcm1haWQtdG8tZXhjYWxpZHJhdyAubWFya2Vye2ZpbGw6IzMzMzMzMztzdHJva2U6IzMzMzMzMzt9I21lcm1haWQtdG8tZXhjYWxpZHJhdyAubWFya2VyLmNyb3Nze3N0cm9rZTojMzMzMzMzO30jbWVybWFpZC10by1leGNhbGlkcmF3IHN2Z3tmb250LWZhbWlseToidHJlYnVjaGV0IG1zIix2ZXJkYW5hLGFyaWFsLHNhbnMtc2VyaWY7Zm9udC1zaXplOjI1cHg7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLmVudGl0eUJveHtmaWxsOiNFQ0VDRkY7c3Ryb2tlOiM5MzcwREI7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLmF0dHJpYnV0ZUJveE9kZHtmaWxsOiNmZmZmZmY7c3Ryb2tlOiM5MzcwREI7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLmF0dHJpYnV0ZUJveEV2ZW57ZmlsbDojZjJmMmYyO3N0cm9rZTojOTM3MERCO30jbWVybWFpZC10by1leGNhbGlkcmF3IC5yZWxhdGlvbnNoaXBMYWJlbEJveHtmaWxsOmhzbCg4MCwgMTAwJSwgOTYuMjc0NTA5ODAzOSUpO29wYWNpdHk6MC43O2JhY2tncm91bmQtY29sb3I6aHNsKDgwLCAxMDAlLCA5Ni4yNzQ1MDk4MDM5JSk7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLnJlbGF0aW9uc2hpcExhYmVsQm94IHJlY3R7b3BhY2l0eTowLjU7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLnJlbGF0aW9uc2hpcExpbmV7c3Ryb2tlOiMzMzMzMzM7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgLmVudGl0eVRpdGxlVGV4dHt0ZXh0LWFuY2hvcjptaWRkbGU7Zm9udC1zaXplOjE4cHg7ZmlsbDojMzMzO30jbWVybWFpZC10by1leGNhbGlkcmF3ICNNRF9QQVJFTlRfU1RBUlR7ZmlsbDojZjVmNWY1IWltcG9ydGFudDtzdHJva2U6IzMzMzMzMyFpbXBvcnRhbnQ7c3Ryb2tlLXdpZHRoOjE7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgI01EX1BBUkVOVF9FTkR7ZmlsbDojZjVmNWY1IWltcG9ydGFudDtzdHJva2U6IzMzMzMzMyFpbXBvcnRhbnQ7c3Ryb2tlLXdpZHRoOjE7fSNtZXJtYWlkLXRvLWV4Y2FsaWRyYXcgOnJvb3R7LS1tZXJtYWlkLWZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjt9PC9zdHlsZT48Zy8+PGRlZnM+PG1hcmtlciBvcmllbnQ9ImF1dG8iIG1hcmtlckhlaWdodD0iMjQwIiBtYXJrZXJXaWR0aD0iMTkwIiByZWZZPSI3IiByZWZYPSIwIiBpZD0iTURfUEFSRU5UX1NUQVJUIj48cGF0aCBkPSJNIDE4LDcgTDksMTMgTDEsNyBMOSwxIFoiLz48L21hcmtlcj48L2RlZnM+PGRlZnM+PG1hcmtlciBvcmllbnQ9ImF1dG8iIG1hcmtlckhlaWdodD0iMjgiIG1hcmtlcldpZHRoPSIyMCIgcmVmWT0iNyIgcmVmWD0iMTkiIGlkPSJNRF9QQVJFTlRfRU5EIj48cGF0aCBkPSJNIDE4LDcgTDksMTMgTDEsNyBMOSwxIFoiLz48L21hcmtlcj48L2RlZnM+PGRlZnM+PG1hcmtlciBvcmllbnQ9ImF1dG8iIG1hcmtlckhlaWdodD0iMTgiIG1hcmtlcldpZHRoPSIxOCIgcmVmWT0iOSIgcmVmWD0iMCIgaWQ9Ik9OTFlfT05FX1NUQVJUIj48cGF0aCBkPSJNOSwwIEw5LDE4IE0xNSwwIEwxNSwxOCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJncmF5Ii8+PC9tYXJrZXI+PC9kZWZzPjxkZWZzPjxtYXJrZXIgb3JpZW50PSJhdXRvIiBtYXJrZXJIZWlnaHQ9IjE4IiBtYXJrZXJXaWR0aD0iMTgiIHJlZlk9IjkiIHJlZlg9IjE4IiBpZD0iT05MWV9PTkVfRU5EIj48cGF0aCBkPSJNMywwIEwzLDE4IE05LDAgTDksMTgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iZ3JheSIvPjwvbWFya2VyPjwvZGVmcz48ZGVmcz48bWFya2VyIG9yaWVudD0iYXV0byIgbWFya2VySGVpZ2h0PSIxOCIgbWFya2VyV2lkdGg9IjMwIiByZWZZPSI5IiByZWZYPSIwIiBpZD0iWkVST19PUl9PTkVfU1RBUlQiPjxjaXJjbGUgcj0iNiIgY3k9IjkiIGN4PSIyMSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iZ3JheSIvPjxwYXRoIGQ9Ik05LDAgTDksMTgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iZ3JheSIvPjwvbWFya2VyPjwvZGVmcz48ZGVmcz48bWFya2VyIG9yaWVudD0iYXV0byIgbWFya2VySGVpZ2h0PSIxOCIgbWFya2VyV2lkdGg9IjMwIiByZWZZPSI5IiByZWZYPSIzMCIgaWQ9IlpFUk9fT1JfT05FX0VORCI+PGNpcmNsZSByPSI2IiBjeT0iOSIgY3g9IjkiIGZpbGw9IndoaXRlIiBzdHJva2U9ImdyYXkiLz48cGF0aCBkPSJNMjEsMCBMMjEsMTgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iZ3JheSIvPjwvbWFya2VyPjwvZGVmcz48ZGVmcz48bWFya2VyIG9yaWVudD0iYXV0byIgbWFya2VySGVpZ2h0PSIzNiIgbWFya2VyV2lkdGg9IjQ1IiByZWZZPSIxOCIgcmVmWD0iMTgiIGlkPSJPTkVfT1JfTU9SRV9TVEFSVCI+PHBhdGggZD0iTTAsMTggUSAxOCwwIDM2LDE4IFEgMTgsMzYgMCwxOCBNNDIsOSBMNDIsMjciIGZpbGw9Im5vbmUiIHN0cm9rZT0iZ3JheSIvPjwvbWFya2VyPjwvZGVmcz48ZGVmcz48bWFya2VyIG9yaWVudD0iYXV0byIgbWFya2VySGVpZ2h0PSIzNiIgbWFya2VyV2lkdGg9IjQ1IiByZWZZPSIxOCIgcmVmWD0iMjciIGlkPSJPTkVfT1JfTU9SRV9FTkQiPjxwYXRoIGQ9Ik0zLDkgTDMsMjcgTTksMTggUTI3LDAgNDUsMTggUTI3LDM2IDksMTgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iZ3JheSIvPjwvbWFya2VyPjwvZGVmcz48ZGVmcz48bWFya2VyIG9yaWVudD0iYXV0byIgbWFya2VySGVpZ2h0PSIzNiIgbWFya2VyV2lkdGg9IjU3IiByZWZZPSIxOCIgcmVmWD0iMTgiIGlkPSJaRVJPX09SX01PUkVfU1RBUlQiPjxjaXJjbGUgcj0iNiIgY3k9IjE4IiBjeD0iNDgiIGZpbGw9IndoaXRlIiBzdHJva2U9ImdyYXkiLz48cGF0aCBkPSJNMCwxOCBRMTgsMCAzNiwxOCBRMTgsMzYgMCwxOCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJncmF5Ii8+PC9tYXJrZXI+PC9kZWZzPjxkZWZzPjxtYXJrZXIgb3JpZW50PSJhdXRvIiBtYXJrZXJIZWlnaHQ9IjM2IiBtYXJrZXJXaWR0aD0iNTciIHJlZlk9IjE4IiByZWZYPSIzOSIgaWQ9IlpFUk9fT1JfTU9SRV9FTkQiPjxjaXJjbGUgcj0iNiIgY3k9IjE4IiBjeD0iOSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iZ3JheSIvPjxwYXRoIGQ9Ik0yMSwxOCBRMzksMCA1NywxOCBRMzksMzYgMjEsMTgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iZ3JheSIvPjwvbWFya2VyPjwvZGVmcz48cGF0aCBzdHlsZT0ic3Ryb2tlOiBncmF5OyBmaWxsOiBub25lOyIgbWFya2VyLXN0YXJ0PSJ1cmwoI09OTFlfT05FX1NUQVJUKSIgbWFya2VyLWVuZD0idXJsKCNaRVJPX09SX01PUkVfRU5EKSIgZD0iTTEwMy43MDksMTQ5TDEwMy43MDksMTYwLjgzM0MxMDMuNzA5LDE3Mi42NjcsMTAzLjcwOSwxOTYuMzMzLDExNC40NSwyMTYuNUMxMjUuMTkxLDIzNi42NjcsMTQ2LjY3MiwyNTMuMzMzLDE1Ny40MTMsMjYxLjY2N0wxNjguMTU0LDI3MCIgY2xhc3M9ImVyIHJlbGF0aW9uc2hpcExpbmUiLz48cGF0aCBzdHlsZT0ic3Ryb2tlOiBncmF5OyBmaWxsOiBub25lOyIgbWFya2VyLXN0YXJ0PSJ1cmwoI09OTFlfT05FX1NUQVJUKSIgbWFya2VyLWVuZD0idXJsKCNaRVJPX09SX01PUkVfRU5EKSIgZD0iTTM3MS44MDEsMTcwTDM3MS44MDEsMTc4LjMzM0MzNzEuODAxLDE4Ni42NjcsMzcxLjgwMSwyMDMuMzMzLDM2MS4wNiwyMjBDMzUwLjMxOSwyMzYuNjY3LDMyOC44MzcsMjUzLjMzMywzMTguMDk2LDI2MS42NjdMMzA3LjM1NiwyNzAiIGNsYXNzPSJlciByZWxhdGlvbnNoaXBMaW5lIi8+PHBhdGggc3R5bGU9InN0cm9rZTogZ3JheTsgZmlsbDogbm9uZTsiIG1hcmtlci1zdGFydD0idXJsKCNPTkxZX09ORV9TVEFSVCkiIG1hcmtlci1lbmQ9InVybCgjWkVST19PUl9NT1JFX0VORCkiIGQ9Ik02MjkuMTM2LDE1OS41TDYyOS4xMzYsMTY5LjU4M0M2MjkuMTM2LDE3OS42NjcsNjI5LjEzNiwxOTkuODMzLDYzOC45OTIsMjE4LjI1QzY0OC44NDcsMjM2LjY2Nyw2NjguNTU5LDI1My4zMzMsNjc4LjQxNCwyNjEuNjY3TDY4OC4yNywyNzAiIGNsYXNzPSJlciByZWxhdGlvbnNoaXBMaW5lIi8+PHBhdGggc3R5bGU9InN0cm9rZTogZ3JheTsgZmlsbDogbm9uZTsiIG1hcmtlci1zdGFydD0idXJsKCNPTkxZX09ORV9TVEFSVCkiIG1hcmtlci1lbmQ9InVybCgjWkVST19PUl9NT1JFX0VORCkiIGQ9Ik04NzUuMTM1LDE3MEw4NzUuMTM1LDE3OC4zMzNDODc1LjEzNSwxODYuNjY3LDg3NS4xMzUsMjAzLjMzMyw4NjUuMjc5LDIyMEM4NTUuNDIzLDIzNi42NjcsODM1LjcxMiwyNTMuMzMzLDgyNS44NTYsMjYxLjY2N0w4MTYsMjcwIiBjbGFzcz0iZXIgcmVsYXRpb25zaGlwTGluZSIvPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIwLDQxICkiIGlkPSJlbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50LThkYWFkMTFmLTBmYTUtNThiZi1iZWE4LWJiNjg3ZmViNTA4YSI+PHJlY3QgaGVpZ2h0PSIxMDgiIHdpZHRoPSIxNjcuNDE3OTY4NzUiIHk9IjAiIHg9IjAiIGNsYXNzPSJlciBlbnRpdHlCb3giLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgZm9udC1zaXplOiAxMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODMuNzA4OTg0Mzc1LDEyKSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudC04ZGFhZDExZi0wZmE1LTU4YmYtYmVhOC1iYjY4N2ZlYjUwOGEiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+YW5vbnltaXphdGlvbl9kb2N1bWVudDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguMTIzNzU4OTUxODIyOTIiIHk9IjI0IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSwzNC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudC04ZGFhZDExZi0wZmE1LTU4YmYtYmVhOC1iYjY4N2ZlYjUwOGEtYXR0ci0xLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VVVJRDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguMzI4MDI4MzYxMDAyNjEiIHk9IjI0IiB4PSI2OC4xMjM3NTg5NTE4MjI5MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDczLjEyMzc1ODk1MTgyMjkyLDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50LThkYWFkMTFmLTBmYTUtNThiZi1iZWE4LWJiNjg3ZmViNTA4YS1hdHRyLTEtbmFtZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5pZDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMzAuOTY2MTgxNDM3MTc0NDgiIHk9IjI0IiB4PSIxMzYuNDUxNzg3MzEyODI1NTMiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDEuNDUxNzg3MzEyODI1NTMsMzQuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9uZG9jdW1lbnQtOGRhYWQxMWYtMGZhNS01OGJmLWJlYTgtYmI2ODdmZWI1MDhhLWF0dHItMS1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+UEs8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjY4LjEyMzc1ODk1MTgyMjkyIiB5PSI0NSIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDU1LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50LThkYWFkMTFmLTBmYTUtNThiZi1iZWE4LWJiNjg3ZmViNTA4YS1hdHRyLTItdHlwZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5EQVRFVElNRTwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguMzI4MDI4MzYxMDAyNjEiIHk9IjQ1IiB4PSI2OC4xMjM3NTg5NTE4MjI5MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3My4xMjM3NTg5NTE4MjI5Miw1NS41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudC04ZGFhZDExZi0wZmE1LTU4YmYtYmVhOC1iYjY4N2ZlYjUwOGEtYXR0ci0yLW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+Y3JlYXRlZF9hdDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMzAuOTY2MTgxNDM3MTc0NDgiIHk9IjQ1IiB4PSIxMzYuNDUxNzg3MzEyODI1NTMiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQxLjQ1MTc4NzMxMjgyNTUzLDU1LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50LThkYWFkMTFmLTBmYTUtNThiZi1iZWE4LWJiNjg3ZmViNTA4YS1hdHRyLTIta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguMTIzNzU4OTUxODIyOTIiIHk9IjY2IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudC04ZGFhZDExZi0wZmE1LTU4YmYtYmVhOC1iYjY4N2ZlYjUwOGEtYXR0ci0zLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+REFURVRJTUU8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjY4LjMyODAyODM2MTAwMjYxIiB5PSI2NiIgeD0iNjguMTIzNzU4OTUxODIyOTIiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3My4xMjM3NTg5NTE4MjI5Miw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudC04ZGFhZDExZi0wZmE1LTU4YmYtYmVhOC1iYjY4N2ZlYjUwOGEtYXR0ci0zLW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+dXBkYXRlZF9hdDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMzAuOTY2MTgxNDM3MTc0NDgiIHk9IjY2IiB4PSIxMzYuNDUxNzg3MzEyODI1NTMiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDEuNDUxNzg3MzEyODI1NTMsNzYuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9uZG9jdW1lbnQtOGRhYWQxMWYtMGZhNS01OGJmLWJlYTgtYmI2ODdmZWI1MDhhLWF0dHItMy1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCIvPjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2OC4xMjM3NTg5NTE4MjI5MiIgeT0iODciIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw5Ny41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudC04ZGFhZDExZi0wZmE1LTU4YmYtYmVhOC1iYjY4N2ZlYjUwOGEtYXR0ci00LXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VEVYVDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguMzI4MDI4MzYxMDAyNjEiIHk9Ijg3IiB4PSI2OC4xMjM3NTg5NTE4MjI5MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3My4xMjM3NTg5NTE4MjI5Miw5Ny41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudC04ZGFhZDExZi0wZmE1LTU4YmYtYmVhOC1iYjY4N2ZlYjUwOGEtYXR0ci00LW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+bmFtZTwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMzAuOTY2MTgxNDM3MTc0NDgiIHk9Ijg3IiB4PSIxMzYuNDUxNzg3MzEyODI1NTMiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQxLjQ1MTc4NzMxMjgyNTUzLDk3LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50LThkYWFkMTFmLTBmYTUtNThiZi1iZWE4LWJiNjg3ZmViNTA4YS1hdHRyLTQta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48L2c+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjg3LjQxNzk2ODc1LDIwICkiIGlkPSJlbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMiPjxyZWN0IGhlaWdodD0iMTUwIiB3aWR0aD0iMTY4Ljc2NTYyNSIgeT0iMCIgeD0iMCIgY2xhc3M9ImVyIGVudGl0eUJveCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyB0ZXh0LWFuY2hvcjogbWlkZGxlOyBmb250LXNpemU6IDEycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4NC4zODI4MTI1LDEyKSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25wYXJhZ3JhcGgtZjVjNjUzOTMtNTA2MC01NDMyLWIzNTctOGY5MzVhNThhMWUzIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmFub255bWl6YXRpb25fcGFyYWdyYXBoPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2OC41NzI5Nzc3MDE4MjI5MiIgeT0iMjQiIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci0xLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VVVJRDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguNzc3MjQ3MTExMDAyNjEiIHk9IjI0IiB4PSI2OC41NzI5Nzc3MDE4MjI5MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDczLjU3Mjk3NzcwMTgyMjkyLDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci0xLW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+aWQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjMxLjQxNTQwMDE4NzE3NDQ4IiB5PSIyNCIgeD0iMTM3LjM1MDIyNDgxMjgyNTUzIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQyLjM1MDIyNDgxMjgyNTUzLDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci0xLWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5QSzwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguNTcyOTc3NzAxODIyOTIiIHk9IjQ1IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsNTUuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9ucGFyYWdyYXBoLWY1YzY1MzkzLTUwNjAtNTQzMi1iMzU3LThmOTM1YTU4YTFlMy1hdHRyLTItdHlwZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5URVhUPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2OC43NzcyNDcxMTEwMDI2MSIgeT0iNDUiIHg9IjY4LjU3Mjk3NzcwMTgyMjkyIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDczLjU3Mjk3NzcwMTgyMjkyLDU1LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci0yLW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+dGV4dDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMzEuNDE1NDAwMTg3MTc0NDgiIHk9IjQ1IiB4PSIxMzcuMzUwMjI0ODEyODI1NTMiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQyLjM1MDIyNDgxMjgyNTUzLDU1LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci0yLWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIi8+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjY4LjU3Mjk3NzcwMTgyMjkyIiB5PSI2NiIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsNzYuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9ucGFyYWdyYXBoLWY1YzY1MzkzLTUwNjAtNTQzMi1iMzU3LThmOTM1YTU4YTFlMy1hdHRyLTMtdHlwZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5KU09OPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2OC43NzcyNDcxMTEwMDI2MSIgeT0iNjYiIHg9IjY4LjU3Mjk3NzcwMTgyMjkyIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzMuNTcyOTc3NzAxODIyOTIsNzYuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9ucGFyYWdyYXBoLWY1YzY1MzkzLTUwNjAtNTQzMi1iMzU3LThmOTM1YTU4YTFlMy1hdHRyLTMtbmFtZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5wcmVkaWN0aW9uPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSIzMS40MTU0MDAxODcxNzQ0OCIgeT0iNjYiIHg9IjEzNy4zNTAyMjQ4MTI4MjU1MyIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE0Mi4zNTAyMjQ4MTI4MjU1Myw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25wYXJhZ3JhcGgtZjVjNjUzOTMtNTA2MC01NDMyLWIzNTctOGY5MzVhNThhMWUzLWF0dHItMy1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCIvPjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2OC41NzI5Nzc3MDE4MjI5MiIgeT0iODciIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw5Ny41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25wYXJhZ3JhcGgtZjVjNjUzOTMtNTA2MC01NDMyLWIzNTctOGY5MzVhNThhMWUzLWF0dHItNC10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPkpTT048L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjY4Ljc3NzI0NzExMTAwMjYxIiB5PSI4NyIgeD0iNjguNTcyOTc3NzAxODIyOTIiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzMuNTcyOTc3NzAxODIyOTIsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9ucGFyYWdyYXBoLWY1YzY1MzkzLTUwNjAtNTQzMi1iMzU3LThmOTM1YTU4YTFlMy1hdHRyLTQtbmFtZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj52YWxpZGF0aW9uPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSIzMS40MTU0MDAxODcxNzQ0OCIgeT0iODciIHg9IjEzNy4zNTAyMjQ4MTI4MjU1MyIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDIuMzUwMjI0ODEyODI1NTMsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9ucGFyYWdyYXBoLWY1YzY1MzkzLTUwNjAtNTQzMi1iMzU3LThmOTM1YTU4YTFlMy1hdHRyLTQta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguNTcyOTc3NzAxODIyOTIiIHk9IjEwOCIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsMTE4LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci01LXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+REFURVRJTUU8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjY4Ljc3NzI0NzExMTAwMjYxIiB5PSIxMDgiIHg9IjY4LjU3Mjk3NzcwMTgyMjkyIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzMuNTcyOTc3NzAxODIyOTIsMTE4LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci01LW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+Y3JlYXRlZF9hdDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMzEuNDE1NDAwMTg3MTc0NDgiIHk9IjEwOCIgeD0iMTM3LjM1MDIyNDgxMjgyNTUzIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQyLjM1MDIyNDgxMjgyNTUzLDExOC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25wYXJhZ3JhcGgtZjVjNjUzOTMtNTA2MC01NDMyLWIzNTctOGY5MzVhNThhMWUzLWF0dHItNS1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCIvPjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2OC41NzI5Nzc3MDE4MjI5MiIgeT0iMTI5IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsMTM5LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci02LXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+REFURVRJTUU8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjY4Ljc3NzI0NzExMTAwMjYxIiB5PSIxMjkiIHg9IjY4LjU3Mjk3NzcwMTgyMjkyIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDczLjU3Mjk3NzcwMTgyMjkyLDEzOS41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25wYXJhZ3JhcGgtZjVjNjUzOTMtNTA2MC01NDMyLWIzNTctOGY5MzVhNThhMWUzLWF0dHItNi1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPnVwZGF0ZWRfYXQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjMxLjQxNTQwMDE4NzE3NDQ4IiB5PSIxMjkiIHg9IjEzNy4zNTAyMjQ4MTI4MjU1MyIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDIuMzUwMjI0ODEyODI1NTMsMTM5LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbnBhcmFncmFwaC1mNWM2NTM5My01MDYwLTU0MzItYjM1Ny04ZjkzNWE1OGExZTMtYXR0ci02LWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIi8+PC9nPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyMy42ODQ1NzAzMTI1LDI3MCApIiBpZD0iZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudHBhcmFncmFwaC05MGMwMzBmMC1mNzYzLTU3NWUtOGU3Ni0yNmU1M2Y4YjRmYzkiPjxyZWN0IGhlaWdodD0iMTA4IiB3aWR0aD0iMjI4LjE0MDYyNSIgeT0iMCIgeD0iMCIgY2xhc3M9ImVyIGVudGl0eUJveCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyB0ZXh0LWFuY2hvcjogbWlkZGxlOyBmb250LXNpemU6IDEycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMTQuMDcwMzEyNSwxMikiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9uZG9jdW1lbnRwYXJhZ3JhcGgtOTBjMDMwZjAtZjc2My01NzVlLThlNzYtMjZlNTNmOGI0ZmM5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmFub255bWl6YXRpb25fZG9jdW1lbnRfcGFyYWdyYXBoPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI3Ni4yMzg3NTkzNTg3MjM5NSIgeT0iMjQiIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50cGFyYWdyYXBoLTkwYzAzMGYwLWY3NjMtNTc1ZS04ZTc2LTI2ZTUzZjhiNGZjOS1hdHRyLTEtdHlwZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5VVUlEPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI5MS4zMTY2NzA3MzU2NzcwOCIgeT0iMjQiIHg9Ijc2LjIzODc1OTM1ODcyMzk1IiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODEuMjM4NzU5MzU4NzIzOTUsMzQuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9uZG9jdW1lbnRwYXJhZ3JhcGgtOTBjMDMwZjAtZjc2My01NzVlLThlNzYtMjZlNTNmOGI0ZmM5LWF0dHItMS1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmlkPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2MC41ODUxOTQ5MDU1OTg5NTQiIHk9IjI0IiB4PSIxNjcuNTU1NDMwMDk0NDAxMDMiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNzIuNTU1NDMwMDk0NDAxMDMsMzQuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9uZG9jdW1lbnRwYXJhZ3JhcGgtOTBjMDMwZjAtZjc2My01NzVlLThlNzYtMjZlNTNmOGI0ZmM5LWF0dHItMS1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+UEs8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9Ijc2LjIzODc1OTM1ODcyMzk1IiB5PSI0NSIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDU1LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50cGFyYWdyYXBoLTkwYzAzMGYwLWY3NjMtNTc1ZS04ZTc2LTI2ZTUzZjhiNGZjOS1hdHRyLTItdHlwZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5VVUlEPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI5MS4zMTY2NzA3MzU2NzcwOCIgeT0iNDUiIHg9Ijc2LjIzODc1OTM1ODcyMzk1IiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDgxLjIzODc1OTM1ODcyMzk1LDU1LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50cGFyYWdyYXBoLTkwYzAzMGYwLWY3NjMtNTc1ZS04ZTc2LTI2ZTUzZjhiNGZjOS1hdHRyLTItbmFtZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5kb2N1bWVudF9pZDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjAuNTg1MTk0OTA1NTk4OTU0IiB5PSI0NSIgeD0iMTY3LjU1NTQzMDA5NDQwMTAzIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE3Mi41NTU0MzAwOTQ0MDEwMyw1NS41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudHBhcmFncmFwaC05MGMwMzBmMC1mNzYzLTU3NWUtOGU3Ni0yNmU1M2Y4YjRmYzktYXR0ci0yLWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5QSyxGSzwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNzYuMjM4NzU5MzU4NzIzOTUiIHk9IjY2IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudHBhcmFncmFwaC05MGMwMzBmMC1mNzYzLTU3NWUtOGU3Ni0yNmU1M2Y4YjRmYzktYXR0ci0zLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VVVJRDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iOTEuMzE2NjcwNzM1Njc3MDgiIHk9IjY2IiB4PSI3Ni4yMzg3NTkzNTg3MjM5NSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDgxLjIzODc1OTM1ODcyMzk1LDc2LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50cGFyYWdyYXBoLTkwYzAzMGYwLWY3NjMtNTc1ZS04ZTc2LTI2ZTUzZjhiNGZjOS1hdHRyLTMtbmFtZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5wYXJhZ3JhcGhfaWQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYwLjU4NTE5NDkwNTU5ODk1NCIgeT0iNjYiIHg9IjE2Ny41NTU0MzAwOTQ0MDEwMyIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE3Mi41NTU0MzAwOTQ0MDEwMyw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWFub255bWl6YXRpb25kb2N1bWVudHBhcmFncmFwaC05MGMwMzBmMC1mNzYzLTU3NWUtOGU3Ni0yNmU1M2Y4YjRmYzktYXR0ci0zLWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5QSyxGSzwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNzYuMjM4NzU5MzU4NzIzOTUiIHk9Ijg3IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9uZG9jdW1lbnRwYXJhZ3JhcGgtOTBjMDMwZjAtZjc2My01NzVlLThlNzYtMjZlNTNmOGI0ZmM5LWF0dHItNC10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPklOVEVHRVI8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjkxLjMxNjY3MDczNTY3NzA4IiB5PSI4NyIgeD0iNzYuMjM4NzU5MzU4NzIzOTUiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODEuMjM4NzU5MzU4NzIzOTUsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1hbm9ueW1pemF0aW9uZG9jdW1lbnRwYXJhZ3JhcGgtOTBjMDMwZjAtZjc2My01NzVlLThlNzYtMjZlNTNmOGI0ZmM5LWF0dHItNC1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPm9yZGVyPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2MC41ODUxOTQ5MDU1OTg5NTQiIHk9Ijg3IiB4PSIxNjcuNTU1NDMwMDk0NDAxMDMiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTcyLjU1NTQzMDA5NDQwMTAzLDk3LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktYW5vbnltaXphdGlvbmRvY3VtZW50cGFyYWdyYXBoLTkwYzAzMGYwLWY3NjMtNTc1ZS04ZTc2LTI2ZTUzZjhiNGZjOS1hdHRyLTQta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48L2c+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNTU2LjE4MzU5Mzc1LDMwLjUgKSIgaWQ9ImVudGl0eS1kYXRhcHVibGljZG9jdW1lbnQtYTBiZTViOTQtOTNkNi01MTZiLTg1YWEtNTliNmM2ZTRjODY1Ij48cmVjdCBoZWlnaHQ9IjEyOSIgd2lkdGg9IjE0NS45MDQzNzMxNjg5NDUzIiB5PSIwIiB4PSIwIiBjbGFzcz0iZXIgZW50aXR5Qm94Ii8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IHRleHQtYW5jaG9yOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDcyLjk1MjE4NjU4NDQ3MjY2LDEyKSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudC1hMGJlNWI5NC05M2Q2LTUxNmItODVhYS01OWI2YzZlNGM4NjUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+ZGF0YXB1YmxpY19kb2N1bWVudDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjAuOTUyNTYwNDI0ODA0NjkiIHk9IjI0IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSwzNC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudC1hMGJlNWI5NC05M2Q2LTUxNmItODVhYS01OWI2YzZlNGM4NjUtYXR0ci0xLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VVVJRDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjEuMTU2ODI5ODMzOTg0Mzc1IiB5PSIyNCIgeD0iNjAuOTUyNTYwNDI0ODA0NjkiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NS45NTI1NjA0MjQ4MDQ2OSwzNC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudC1hMGJlNWI5NC05M2Q2LTUxNmItODVhYS01OWI2YzZlNGM4NjUtYXR0ci0xLW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+aWQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjIzLjc5NDk4MjkxMDE1NjI1IiB5PSIyNCIgeD0iMTIyLjEwOTM5MDI1ODc4OTA2IiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI3LjEwOTM5MDI1ODc4OTA2LDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY2RvY3VtZW50LWEwYmU1Yjk0LTkzZDYtNTE2Yi04NWFhLTU5YjZjNmU0Yzg2NS1hdHRyLTEta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPlBLPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2MC45NTI1NjA0MjQ4MDQ2OSIgeT0iNDUiIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1NS41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudC1hMGJlNWI5NC05M2Q2LTUxNmItODVhYS01OWI2YzZlNGM4NjUtYXR0ci0yLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+SlNPTjwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjEuMTU2ODI5ODMzOTg0Mzc1IiB5PSI0NSIgeD0iNjAuOTUyNTYwNDI0ODA0NjkiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjUuOTUyNTYwNDI0ODA0NjksNTUuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnQtYTBiZTViOTQtOTNkNi01MTZiLTg1YWEtNTliNmM2ZTRjODY1LWF0dHItMi1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPnByZWRpY3Rpb248L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjIzLjc5NDk4MjkxMDE1NjI1IiB5PSI0NSIgeD0iMTIyLjEwOTM5MDI1ODc4OTA2IiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyNy4xMDkzOTAyNTg3ODkwNiw1NS41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudC1hMGJlNWI5NC05M2Q2LTUxNmItODVhYS01OWI2YzZlNGM4NjUtYXR0ci0yLWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIi8+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYwLjk1MjU2MDQyNDgwNDY5IiB5PSI2NiIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsNzYuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnQtYTBiZTViOTQtOTNkNi01MTZiLTg1YWEtNTliNmM2ZTRjODY1LWF0dHItMy10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPkpTT048L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYxLjE1NjgyOTgzMzk4NDM3NSIgeT0iNjYiIHg9IjYwLjk1MjU2MDQyNDgwNDY5IiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjUuOTUyNTYwNDI0ODA0NjksNzYuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnQtYTBiZTViOTQtOTNkNi01MTZiLTg1YWEtNTliNmM2ZTRjODY1LWF0dHItMy1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPnZhbGlkYXRpb248L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjIzLjc5NDk4MjkxMDE1NjI1IiB5PSI2NiIgeD0iMTIyLjEwOTM5MDI1ODc4OTA2IiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI3LjEwOTM5MDI1ODc4OTA2LDc2LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY2RvY3VtZW50LWEwYmU1Yjk0LTkzZDYtNTE2Yi04NWFhLTU5YjZjNmU0Yzg2NS1hdHRyLTMta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjAuOTUyNTYwNDI0ODA0NjkiIHk9Ijg3IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnQtYTBiZTViOTQtOTNkNi01MTZiLTg1YWEtNTliNmM2ZTRjODY1LWF0dHItNC10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPkRBVEVUSU1FPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2MS4xNTY4Mjk4MzM5ODQzNzUiIHk9Ijg3IiB4PSI2MC45NTI1NjA0MjQ4MDQ2OSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NS45NTI1NjA0MjQ4MDQ2OSw5Ny41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudC1hMGJlNWI5NC05M2Q2LTUxNmItODVhYS01OWI2YzZlNGM4NjUtYXR0ci00LW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+Y3JlYXRlZF9hdDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMjMuNzk0OTgyOTEwMTU2MjUiIHk9Ijg3IiB4PSIxMjIuMTA5MzkwMjU4Nzg5MDYiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI3LjEwOTM5MDI1ODc4OTA2LDk3LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY2RvY3VtZW50LWEwYmU1Yjk0LTkzZDYtNTE2Yi04NWFhLTU5YjZjNmU0Yzg2NS1hdHRyLTQta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjAuOTUyNTYwNDI0ODA0NjkiIHk9IjEwOCIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsMTE4LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY2RvY3VtZW50LWEwYmU1Yjk0LTkzZDYtNTE2Yi04NWFhLTU5YjZjNmU0Yzg2NS1hdHRyLTUtdHlwZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5EQVRFVElNRTwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjEuMTU2ODI5ODMzOTg0Mzc1IiB5PSIxMDgiIHg9IjYwLjk1MjU2MDQyNDgwNDY5IiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjUuOTUyNTYwNDI0ODA0NjksMTE4LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY2RvY3VtZW50LWEwYmU1Yjk0LTkzZDYtNTE2Yi04NWFhLTU5YjZjNmU0Yzg2NS1hdHRyLTUtbmFtZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj51cGRhdGVkX2F0PC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSIyMy43OTQ5ODI5MTAxNTYyNSIgeT0iMTA4IiB4PSIxMjIuMTA5MzkwMjU4Nzg5MDYiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjcuMTA5MzkwMjU4Nzg5MDYsMTE4LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY2RvY3VtZW50LWEwYmU1Yjk0LTkzZDYtNTE2Yi04NWFhLTU5YjZjNmU0Yzg2NS1hdHRyLTUta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48L2c+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODAyLjA4Nzk2NjkxODk0NTMsMjAgKSIgaWQ9ImVudGl0eS1kYXRhcHVibGljcGFyYWdyYXBoLWE3YTE2NDVlLWEzZTctNTIzNy04M2NlLTMxYTVkNzUwNjg4YiI+PHJlY3QgaGVpZ2h0PSIxNTAiIHdpZHRoPSIxNDYuMDkzNzUiIHk9IjAiIHg9IjAiIGNsYXNzPSJlciBlbnRpdHlCb3giLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgZm9udC1zaXplOiAxMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzMuMDQ2ODc1LDEyKSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmRhdGFwdWJsaWNfcGFyYWdyYXBoPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2MS4wMTU2ODYwMzUxNTYyNSIgeT0iMjQiIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY3BhcmFncmFwaC1hN2ExNjQ1ZS1hM2U3LTUyMzctODNjZS0zMWE1ZDc1MDY4OGItYXR0ci0xLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VVVJRDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjEuMjE5OTU1NDQ0MzM1OTQiIHk9IjI0IiB4PSI2MS4wMTU2ODYwMzUxNTYyNSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY2LjAxNTY4NjAzNTE1NjI1LDM0LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY3BhcmFncmFwaC1hN2ExNjQ1ZS1hM2U3LTUyMzctODNjZS0zMWE1ZDc1MDY4OGItYXR0ci0xLW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+aWQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjIzLjg1ODEwODUyMDUwNzgxMiIgeT0iMjQiIHg9IjEyMi4yMzU2NDE0Nzk0OTIxOSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyNy4yMzU2NDE0Nzk0OTIxOSwzNC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItMS1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+UEs8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYxLjAxNTY4NjAzNTE1NjI1IiB5PSI0NSIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDU1LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY3BhcmFncmFwaC1hN2ExNjQ1ZS1hM2U3LTUyMzctODNjZS0zMWE1ZDc1MDY4OGItYXR0ci0yLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VEVYVDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjEuMjE5OTU1NDQ0MzM1OTQiIHk9IjQ1IiB4PSI2MS4wMTU2ODYwMzUxNTYyNSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2Ni4wMTU2ODYwMzUxNTYyNSw1NS41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItMi1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPnRleHQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjIzLjg1ODEwODUyMDUwNzgxMiIgeT0iNDUiIHg9IjEyMi4yMzU2NDE0Nzk0OTIxOSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjcuMjM1NjQxNDc5NDkyMTksNTUuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljcGFyYWdyYXBoLWE3YTE2NDVlLWEzZTctNTIzNy04M2NlLTMxYTVkNzUwNjg4Yi1hdHRyLTIta2V5IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiLz48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjEuMDE1Njg2MDM1MTU2MjUiIHk9IjY2IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItMy10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPkpTT048L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYxLjIxOTk1NTQ0NDMzNTk0IiB5PSI2NiIgeD0iNjEuMDE1Njg2MDM1MTU2MjUiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2Ni4wMTU2ODYwMzUxNTYyNSw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItMy1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPnByZWRpY3Rpb248L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjIzLjg1ODEwODUyMDUwNzgxMiIgeT0iNjYiIHg9IjEyMi4yMzU2NDE0Nzk0OTIxOSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyNy4yMzU2NDE0Nzk0OTIxOSw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItMy1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCIvPjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2MS4wMTU2ODYwMzUxNTYyNSIgeT0iODciIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw5Ny41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItNC10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPkpTT048L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYxLjIxOTk1NTQ0NDMzNTk0IiB5PSI4NyIgeD0iNjEuMDE1Njg2MDM1MTU2MjUiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjYuMDE1Njg2MDM1MTU2MjUsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljcGFyYWdyYXBoLWE3YTE2NDVlLWEzZTctNTIzNy04M2NlLTMxYTVkNzUwNjg4Yi1hdHRyLTQtbmFtZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj52YWxpZGF0aW9uPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSIyMy44NTgxMDg1MjA1MDc4MTIiIHk9Ijg3IiB4PSIxMjIuMjM1NjQxNDc5NDkyMTkiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI3LjIzNTY0MTQ3OTQ5MjE5LDk3LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY3BhcmFncmFwaC1hN2ExNjQ1ZS1hM2U3LTUyMzctODNjZS0zMWE1ZDc1MDY4OGItYXR0ci00LWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIi8+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYxLjAxNTY4NjAzNTE1NjI1IiB5PSIxMDgiIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDExOC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItNS10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPkRBVEVUSU1FPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2MS4yMTk5NTU0NDQzMzU5NCIgeT0iMTA4IiB4PSI2MS4wMTU2ODYwMzUxNTYyNSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY2LjAxNTY4NjAzNTE1NjI1LDExOC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNwYXJhZ3JhcGgtYTdhMTY0NWUtYTNlNy01MjM3LTgzY2UtMzFhNWQ3NTA2ODhiLWF0dHItNS1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmNyZWF0ZWRfYXQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjIzLjg1ODEwODUyMDUwNzgxMiIgeT0iMTA4IiB4PSIxMjIuMjM1NjQxNDc5NDkyMTkiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hPZGQiLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjcuMjM1NjQxNDc5NDkyMTksMTE4LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY3BhcmFncmFwaC1hN2ExNjQ1ZS1hM2U3LTUyMzctODNjZS0zMWE1ZDc1MDY4OGItYXR0ci01LWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIi8+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjYxLjAxNTY4NjAzNTE1NjI1IiB5PSIxMjkiIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSwxMzkuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljcGFyYWdyYXBoLWE3YTE2NDVlLWEzZTctNTIzNy04M2NlLTMxYTVkNzUwNjg4Yi1hdHRyLTYtdHlwZSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5EQVRFVElNRTwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjEuMjE5OTU1NDQ0MzM1OTQiIHk9IjEyOSIgeD0iNjEuMDE1Njg2MDM1MTU2MjUiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjYuMDE1Njg2MDM1MTU2MjUsMTM5LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY3BhcmFncmFwaC1hN2ExNjQ1ZS1hM2U3LTUyMzctODNjZS0zMWE1ZDc1MDY4OGItYXR0ci02LW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+dXBkYXRlZF9hdDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iMjMuODU4MTA4NTIwNTA3ODEyIiB5PSIxMjkiIHg9IjEyMi4yMzU2NDE0Nzk0OTIxOSIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjcuMjM1NjQxNDc5NDkyMTksMTM5LjUpIiB5PSIwIiB4PSIwIiBpZD0idGV4dC1lbnRpdHktZGF0YXB1YmxpY3BhcmFncmFwaC1hN2ExNjQ1ZS1hM2U3LTUyMzctODNjZS0zMWE1ZDc1MDY4OGItYXR0ci02LWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIi8+PC9nPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY0OS40MDA5MzYxMjY3MDksMjcwICkiIGlkPSJlbnRpdHktZGF0YXB1YmxpY2RvY3VtZW50cGFyYWdyYXBoLWVmMGYzODg3LTJkYmEtNTc2ZS04NjM0LTg1NjI3OGI2OGI0NiI+PHJlY3QgaGVpZ2h0PSIxMDgiIHdpZHRoPSIyMDUuNDY4NzUiIHk9IjAiIHg9IjAiIGNsYXNzPSJlciBlbnRpdHlCb3giLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgZm9udC1zaXplOiAxMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAyLjczNDM3NSwxMikiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnRwYXJhZ3JhcGgtZWYwZjM4ODctMmRiYS01NzZlLTg2MzQtODU2Mjc4YjY4YjQ2IiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmRhdGFwdWJsaWNfZG9jdW1lbnRfcGFyYWdyYXBoPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI2OC42ODE0Njc2OTIwNTczIiB5PSIyNCIgeD0iMCIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUsMzQuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnRwYXJhZ3JhcGgtZWYwZjM4ODctMmRiYS01NzZlLTg2MzQtODU2Mjc4YjY4YjQ2LWF0dHItMS10eXBlIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPlVVSUQ8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjgzLjc1OTM3OTA2OTAxMDQyIiB5PSIyNCIgeD0iNjguNjgxNDY3NjkyMDU3MyIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDczLjY4MTQ2NzY5MjA1NzMsMzQuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnRwYXJhZ3JhcGgtZWYwZjM4ODctMmRiYS01NzZlLTg2MzQtODU2Mjc4YjY4YjQ2LWF0dHItMS1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmlkPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI1My4wMjc5MDMyMzg5MzIyOSIgeT0iMjQiIHg9IjE1Mi40NDA4NDY3NjEwNjc3MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE1Ny40NDA4NDY3NjEwNjc3MiwzNC41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudHBhcmFncmFwaC1lZjBmMzg4Ny0yZGJhLTU3NmUtODYzNC04NTYyNzhiNjhiNDYtYXR0ci0xLWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5QSzwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguNjgxNDY3NjkyMDU3MyIgeT0iNDUiIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1NS41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudHBhcmFncmFwaC1lZjBmMzg4Ny0yZGJhLTU3NmUtODYzNC04NTYyNzhiNjhiNDYtYXR0ci0yLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VVVJRDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iODMuNzU5Mzc5MDY5MDEwNDIiIHk9IjQ1IiB4PSI2OC42ODE0Njc2OTIwNTczIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDczLjY4MTQ2NzY5MjA1NzMsNTUuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnRwYXJhZ3JhcGgtZWYwZjM4ODctMmRiYS01NzZlLTg2MzQtODU2Mjc4YjY4YjQ2LWF0dHItMi1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPmRvY3VtZW50X2lkPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI1My4wMjc5MDMyMzg5MzIyOSIgeT0iNDUiIHg9IjE1Mi40NDA4NDY3NjEwNjc3MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTcuNDQwODQ2NzYxMDY3NzIsNTUuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnRwYXJhZ3JhcGgtZWYwZjM4ODctMmRiYS01NzZlLTg2MzQtODU2Mjc4YjY4YjQ2LWF0dHItMi1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+UEssRks8L3RleHQ+PHJlY3QgaGVpZ2h0PSIyMSIgd2lkdGg9IjY4LjY4MTQ2NzY5MjA1NzMiIHk9IjY2IiB4PSIwIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudHBhcmFncmFwaC1lZjBmMzg4Ny0yZGJhLTU3NmUtODYzNC04NTYyNzhiNjhiNDYtYXR0ci0zLXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+VVVJRDwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iODMuNzU5Mzc5MDY5MDEwNDIiIHk9IjY2IiB4PSI2OC42ODE0Njc2OTIwNTczIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94T2RkIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzMuNjgxNDY3NjkyMDU3Myw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudHBhcmFncmFwaC1lZjBmMzg4Ny0yZGJhLTU3NmUtODYzNC04NTYyNzhiNjhiNDYtYXR0ci0zLW5hbWUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+cGFyYWdyYXBoX2lkPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI1My4wMjc5MDMyMzg5MzIyOSIgeT0iNjYiIHg9IjE1Mi40NDA4NDY3NjEwNjc3MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveE9kZCIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE1Ny40NDA4NDY3NjEwNjc3Miw3Ni41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudHBhcmFncmFwaC1lZjBmMzg4Ny0yZGJhLTU3NmUtODYzNC04NTYyNzhiNjhiNDYtYXR0ci0zLWtleSIgY2xhc3M9ImVyIGVudGl0eUxhYmVsIj5QSyxGSzwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iNjguNjgxNDY3NjkyMDU3MyIgeT0iODciIHg9IjAiIGNsYXNzPSJlciBhdHRyaWJ1dGVCb3hFdmVuIi8+PHRleHQgc3R5bGU9ImRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTAuMnB4OyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw5Ny41KSIgeT0iMCIgeD0iMCIgaWQ9InRleHQtZW50aXR5LWRhdGFwdWJsaWNkb2N1bWVudHBhcmFncmFwaC1lZjBmMzg4Ny0yZGJhLTU3NmUtODYzNC04NTYyNzhiNjhiNDYtYXR0ci00LXR5cGUiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCI+SU5URUdFUjwvdGV4dD48cmVjdCBoZWlnaHQ9IjIxIiB3aWR0aD0iODMuNzU5Mzc5MDY5MDEwNDIiIHk9Ijg3IiB4PSI2OC42ODE0Njc2OTIwNTczIiBjbGFzcz0iZXIgYXR0cmlidXRlQm94RXZlbiIvPjx0ZXh0IHN0eWxlPSJkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEwLjJweDsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDczLjY4MTQ2NzY5MjA1NzMsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnRwYXJhZ3JhcGgtZWYwZjM4ODctMmRiYS01NzZlLTg2MzQtODU2Mjc4YjY4YjQ2LWF0dHItNC1uYW1lIiBjbGFzcz0iZXIgZW50aXR5TGFiZWwiPm9yZGVyPC90ZXh0PjxyZWN0IGhlaWdodD0iMjEiIHdpZHRoPSI1My4wMjc5MDMyMzg5MzIyOSIgeT0iODciIHg9IjE1Mi40NDA4NDY3NjEwNjc3MiIgY2xhc3M9ImVyIGF0dHJpYnV0ZUJveEV2ZW4iLz48dGV4dCBzdHlsZT0iZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMC4ycHg7IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTcuNDQwODQ2NzYxMDY3NzIsOTcuNSkiIHk9IjAiIHg9IjAiIGlkPSJ0ZXh0LWVudGl0eS1kYXRhcHVibGljZG9jdW1lbnRwYXJhZ3JhcGgtZWYwZjM4ODctMmRiYS01NzZlLTg2MzQtODU2Mjc4YjY4YjQ2LWF0dHItNC1rZXkiIGNsYXNzPSJlciBlbnRpdHlMYWJlbCIvPjwvZz48cmVjdCBoZWlnaHQ9IjE0IiB3aWR0aD0iMjQuMDE1NjI1IiB5PSIyMTIuNjYzMDQwMTYxMTMyOCIgeD0iMTA0LjIyOTUwNzQ0NjI4OTA2IiBjbGFzcz0iZXIgcmVsYXRpb25zaGlwTGFiZWxCb3giLz48dGV4dCBzdHlsZT0idGV4dC1hbmNob3I6IG1pZGRsZTsgZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMnB4OyIgeT0iMjE5LjY2MzA0MDE2MTEzMjgiIHg9IjExNi4yMzczMTk5NDYyODkwNiIgaWQ9InJlbDI1IiBjbGFzcz0iZXIgcmVsYXRpb25zaGlwTGFiZWwiPmxpbmtzPC90ZXh0PjxyZWN0IGhlaWdodD0iMTQiIHdpZHRoPSIyNC4wMTU2MjUiIHk9IjIyMS42NzgwNzAwNjgzNTkzOCIgeD0iMzQyLjU2NDE3ODQ2Njc5NjkiIGNsYXNzPSJlciByZWxhdGlvbnNoaXBMYWJlbEJveCIvPjx0ZXh0IHN0eWxlPSJ0ZXh0LWFuY2hvcjogbWlkZGxlOyBkb21pbmFudC1iYXNlbGluZTogbWlkZGxlOyBmb250LXNpemU6IDEycHg7IiB5PSIyMjguNjc4MDcwMDY4MzU5MzgiIHg9IjM1NC41NzE5OTA5NjY3OTY5IiBpZD0icmVsMjYiIGNsYXNzPSJlciByZWxhdGlvbnNoaXBMYWJlbCI+bGlua3M8L3RleHQ+PHJlY3QgaGVpZ2h0PSIxNCIgd2lkdGg9IjI0LjAxNTYyNSIgeT0iMjE2LjI1NzAxOTA0Mjk2ODc1IiB4PSI2MjkuOTIyNjA3NDIxODc1IiBjbGFzcz0iZXIgcmVsYXRpb25zaGlwTGFiZWxCb3giLz48dGV4dCBzdHlsZT0idGV4dC1hbmNob3I6IG1pZGRsZTsgZG9taW5hbnQtYmFzZWxpbmU6IG1pZGRsZTsgZm9udC1zaXplOiAxMnB4OyIgeT0iMjIzLjI1NzAxOTA0Mjk2ODc1IiB4PSI2NDEuOTMwNDE5OTIxODc1IiBpZD0icmVsMjciIGNsYXNzPSJlciByZWxhdGlvbnNoaXBMYWJlbCI+bGlua3M8L3RleHQ+PHJlY3QgaGVpZ2h0PSIxNCIgd2lkdGg9IjI0LjAxNTYyNSIgeT0iMjIwLjc4MTM1NjgxMTUyMzQ0IiB4PSI4NDguMDA0MDg5MzU1NDY4OCIgY2xhc3M9ImVyIHJlbGF0aW9uc2hpcExhYmVsQm94Ii8+PHRleHQgc3R5bGU9InRleHQtYW5jaG9yOiBtaWRkbGU7IGRvbWluYW50LWJhc2VsaW5lOiBtaWRkbGU7IGZvbnQtc2l6ZTogMTJweDsiIHk9IjIyNy43ODEzNTY4MTE1MjM0NCIgeD0iODYwLjAxMTkwMTg1NTQ2ODgiIGlkPSJyZWwyOCIgY2xhc3M9ImVyIHJlbGF0aW9uc2hpcExhYmVsIj5saW5rczwvdGV4dD48L3N2Zz4=", + "version": 2 + } + } +} \ No newline at end of file diff --git a/docs/database/schema.jpg b/docs/database/schema.jpg deleted file mode 100644 index 0c37c647e1a48146973499c9b044b51155c43b66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142043 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%0A6m`Yu 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

m{af)fzwnaALZzhq`;?y5F;IEiq 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 M?la7^>hF>N0>^@i$N&HU diff --git a/docs/database/schema.mmd b/docs/database/schema.mmd new file mode 100644 index 00000000..c45aa17a --- /dev/null +++ b/docs/database/schema.mmd @@ -0,0 +1,53 @@ +erDiagram + anonymization_document { + UUID id PK + DATETIME created_at + DATETIME updated_at + TEXT name + } + + anonymization_paragraph { + UUID id PK + TEXT text + JSON prediction + JSON validation + DATETIME created_at + DATETIME updated_at + } + + anonymization_document_paragraph { + UUID id PK + UUID document_id PK, FK + UUID paragraph_id PK, FK + INTEGER order + } + + datapublic_document { + UUID id PK + JSON prediction + JSON validation + DATETIME created_at + DATETIME updated_at + } + + datapublic_paragraph { + UUID id PK + TEXT text + JSON prediction + JSON validation + DATETIME created_at + DATETIME updated_at + } + + datapublic_document_paragraph { + UUID id PK + UUID document_id PK, FK + UUID paragraph_id PK, FK + INTEGER order + } + + anonymization_document ||--o{ anonymization_document_paragraph : links + anonymization_paragraph ||--o{ anonymization_document_paragraph : links + + datapublic_document ||--o{ datapublic_document_paragraph : links + datapublic_paragraph ||--o{ datapublic_document_paragraph : links diff --git a/docs/database/schema.png b/docs/database/schema.png new file mode 100644 index 0000000000000000000000000000000000000000..522c5e5796995de65d8bef3e98500120b6d80162 GIT binary patch literal 67305 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 `anonymization_document.id` | +| `paragraph_id` | `UUID` | no | FK -> `anonymization_paragraph.id` | +| `order` | `INTEGER` | sí | Orden del párrafo en documento origen | + +La clave primaria es compuesta por `id`, `document_id`, `paragraph_id`. + +### `datapublic_document` +| Columna | Tipo | Nulo | Notas | +|---|---|---|---| +| `id` | `UUID` | no | Primary key (identificador del documento) | +| `prediction` | `JSON` | sí | Payload reservado de predicción a nivel documento; el router público actual no lo escribe | +| `validation` | `JSON` | sí | Payload de validación a nivel documento | +| `created_at` | `DATETIME` | no | Default server `CURRENT_TIMESTAMP` | +| `updated_at` | `DATETIME` | sí | Se actualiza ante cambios | + +### `datapublic_paragraph` +| Columna | Tipo | Nulo | Notas | +|---|---|---|---| +| `id` | `UUID` | no | Primary key, derivada del hash del texto | +| `text` | `TEXT` | no | Texto normalizado del párrafo | +| `prediction` | `JSON` | sí | Predicciones del modelo (`list[DocLabel]`) | +| `validation` | `JSON` | sí | Reservado para validación por párrafo; la ruta pública hoy existe solo como lógica legacy comentada | +| `created_at` | `DATETIME` | no | Default server `CURRENT_TIMESTAMP` | +| `updated_at` | `DATETIME` | sí | Se actualiza ante cambios | + +### `datapublic_document_paragraph` +| Columna | Tipo | Nulo | Notas | +|---|---|---|---| +| `id` | `UUID` | no | Identificador del vínculo | +| `document_id` | `UUID` | no | FK -> `datapublic_document.id` | +| `paragraph_id` | `UUID` | no | FK -> `datapublic_paragraph.id` | +| `order` | `INTEGER` | sí | Orden del párrafo en documento origen | + +La clave primaria es compuesta por `id`, `document_id`, `paragraph_id`. + +## Mapeo endpoint -> persistencia + +### Anonymizer +- `POST /anonymizer/predict` + - Lee `anonymization_paragraph` por UUID de párrafo. + - Escribe `anonymization_paragraph.prediction` cuando el cache está activo. +- `POST /anonymizer/disambiguate` + - Escribe predicciones desambiguadas en `anonymization_paragraph.prediction`. +- `POST /anonymizer/validation` + - Lee `anonymization_paragraph.validation`. +- `POST /anonymizer/anonymize-document` + - Escribe `anonymization_paragraph.validation`. + - Crea `anonymization_document` con clave derivada del hash del contenido binario subido. + - Crea vínculos en `anonymization_document_paragraph`. + +### Data-public +- `POST /datapublic/predict/{document_id}` + - Usa el `document_id` provisto por el cliente como primary key del documento. + - Asegura existencia de `datapublic_document` cuando `use_cache=true`. + - Escribe `datapublic_paragraph.prediction` cuando `use_cache=true`. + - Escribe vínculo en `datapublic_document_paragraph` cuando `use_cache=true`. +- `GET /datapublic/validation/document/{document_id}` + - Lee `datapublic_document.validation`. +- `POST /datapublic/validation/document/{document_id}` + - Hace upsert de `datapublic_document.validation`. + +## Nota legacy +Los módulos de rutas para CRUD de dataset (`/datapublic/dataset/*`) existen en código pero no están montados en el router público. No deben tratarse como API pública activa hasta su exposición en `core.router`. diff --git a/docs/es/entities/README.md b/docs/es/entities/README.md new file mode 100644 index 00000000..5152c66a --- /dev/null +++ b/docs/es/entities/README.md @@ -0,0 +1,17 @@ +# Entidades +Idioma: [English](../../entities/README.md) | **Español** + +Esta sección documenta los catálogos de entidades utilizados por los distintos flujos de AymurAI. + +## Catálogos de entidades por flujo +- Datapublic: [datapublic/README.md](datapublic/README.md) +- Anonymizer: [anonymizer/README.md](anonymizer/README.md) + +## Notas +- Las entidades de `datapublic` describen la taxonomía de extracción de datos públicos usada por el pipeline de producción `full-paragraph`. +- Las entidades de `anonymizer` describen las labels usadas actualmente por el flujo de anonimización para desambiguación y reemplazo. + +## Documentación relacionada +- Índice de documentación: [../README.md](../README.md) +- Índice de pipelines: [../pipelines/README.md](../pipelines/README.md) +- Índice de modelos: [../models/README.md](../models/README.md) diff --git a/docs/es/entities/anonymizer/README.md b/docs/es/entities/anonymizer/README.md new file mode 100644 index 00000000..b4fee224 --- /dev/null +++ b/docs/es/entities/anonymizer/README.md @@ -0,0 +1,45 @@ +# Entidades de Anonymizer +Idioma: [English](../../../entities/anonymizer/README.md) | **Español** + +Catálogo de labels usadas actualmente por el flujo de anonimización. + +## Alcance +Estas labels representan los tipos de entidad reconocidos y transformados por el pipeline de producción `flair-anonymizer`. Son las labels sobre las que operan la desambiguación, las políticas de anonimización y las políticas de renderizado. + +## Labels +| Label | Descripción | +|---|---| +| `PER` | Nombre de persona o mención de persona. | +| `EDAD` | Edad. | +| `DNI` | Número de documento nacional de identidad argentino. | +| `NACIONALIDAD` | Nacionalidad. | +| `ESTUDIOS` | Estudios o nivel educativo. | +| `DIRECCION` | Dirección postal o calle. | +| `LOC` | Ubicación geográfica o referencia de lugar. | +| `TELEFONO` | Número de teléfono. | +| `CORREO_ELECTRONICO` | Dirección de correo electrónico. | +| `FECHA` | Fecha calendario. | +| `NUM_EXPEDIENTE` | Número de expediente o causa. | +| `CUIJ` | Identificador judicial único de causa. | +| `NUM_ACTUACION` | Número de actuación. | +| `NUM_MATRICULA` | Número de matrícula o licencia profesional. | +| `NOMBRE_ARCHIVO` | Nombre de archivo. | +| `TEXTO_ANONIMIZAR` | Fragmento de texto libre marcado explícitamente para anonimización cuando no encaja en una label estructurada más específica. | +| `USUARIX` | Nombre de usuario, handle o identificador de cuenta. | +| `LINK` | URL o enlace web. | +| `IP` | Dirección IP. | +| `CUIT_CUIL` | Identificador tributario o laboral argentino. | +| `BANCO` | Nombre de entidad bancaria. | +| `CBU` | Clave Bancaria Uniforme argentina. | +| `NUM_CAJA_AHORRO` | Número de caja de ahorro. | +| `MARCA_AUTOMOVIL` | Referencia a marca o modelo de vehículo. | +| `PATENTE_DOMINIO` | Patente o dominio vehicular. | + +## Notas +- Este catálogo refleja el conjunto actual de tokens de anonimización usado por las utilidades de reemplazo del backend. +- Cada label puede configurarse con políticas específicas de anonimización o desambiguación mediante `LabelPolicy`. + +## Documentación relacionada +- Índice de entidades: [../README.md](../README.md) +- Pipeline anonymizer: [../../pipelines/anonymizer/README.md](../../pipelines/anonymizer/README.md) +- Referencia API: [../../api/README.md](../../api/README.md) diff --git a/docs/es/entities/datapublic/README.md b/docs/es/entities/datapublic/README.md new file mode 100644 index 00000000..9b9c1c91 --- /dev/null +++ b/docs/es/entities/datapublic/README.md @@ -0,0 +1,47 @@ +# Entidades de Datapublic +Idioma: [English](../../../entities/datapublic/README.md) | **Español** + +Catálogo de entidades utilizadas por el flujo de extracción de datapublic. + +## Alcance +Estas entidades corresponden a la taxonomía extraída de resoluciones judiciales por el pipeline de producción `full-paragraph`. Se usan a lo largo del modelo Flair NER, el postprocesamiento de decisiones, la UI de validación y el tooling relacionado con el dataset. + +## Entidades +| Entidad | Descripción | +|---|---| +| `ART_INFRINGIDO` | Artículo(s) o norma(s) legal(es) infringida(s) en el caso. | +| `CONDUCTA` | Acción asociada al delito, contravención o falta descripta en el artículo infringido. | +| `CONDUCTA_DESCRIPCION` | Caracterización adicional de la conducta, como agravantes o detalles de modalidad. | +| `DECISION` | Fragmento de texto que denota una decisión judicial. En producción es una label sintética emitida por el clasificador de decisiones. | +| `DETALLE` | Detalle que especifica qué se resolvió dentro de `OBJETO_DE_LA_RESOLUCION`. | +| `EDAD_AL_MOMENTO_DEL_HECHO` | Edad de la persona al momento del hecho. | +| `FECHA_DE_NACIMIENTO` | Fecha de nacimiento. | +| `FECHA_DEL_HECHO` | Fecha en que ocurrió el hecho denunciado. | +| `FECHA_RESOLUCION` | Fecha de la resolución; en audiencias orales, la fecha de inicio de la audiencia. | +| `FRASES_AGRESION` | Frases citadas o parafraseadas como agresión verbal dentro de los hechos del caso. | +| `GENERO` | Género. | +| `HIJOS_HIJAS_EN_COMUN` | Si la persona acusada y la denunciante tienen hijos/as en común. | +| `HORA_DE_CIERRE` | Hora de finalización de la audiencia. | +| `HORA_DE_INICIO` | Hora de inicio de la audiencia. | +| `LUGAR_DEL_HECHO` | Lugar físico o mediado donde ocurrieron los hechos. | +| `MODALIDAD_DE_LA_VIOLENCIA` | Modalidad en la que se manifiesta la violencia, como violencia doméstica, institucional, laboral, mediática o en espacio público. | +| `N_EXPTE_EJE` | Identificador del expediente o caso. | +| `NACIONALIDAD` | Nacionalidad. | +| `NIVEL_INSTRUCCION` | Nivel de estudios formales alcanzado por la persona. | +| `NOMBRE` | Nombre de persona. | +| `OBJETO_DE_LA_RESOLUCION` | Sobre qué resolvió el juzgado. | +| `PERSONA_ACUSADA_NO_DETERMINADA` | Parte acusada no determinada o no humana, como una persona jurídica o una cuenta en línea. | +| `RELACION_Y_TIPO_ENTRE_ACUSADO/A_Y_DENUNCIANTE` | Tipo de vínculo entre la persona acusada y la denunciante. | +| `TIPO_DE_RESOLUCION` | Tipo de resolución, como interlocutoria o definitiva. | +| `VIOLENCIA_DE_GENERO` | Si los hechos investigados ocurren en un contexto de violencia de género. | + +## Subcategorías y campos de validación +- Muchas de estas entidades tienen subclasificaciones o vocabularios controlados específicos para validación. +- Las opciones históricas de validación están capturadas en el template de Label Studio: [../../../../resources/annotations/label-studio/datapublic/label-studio-config.xml](../../../../resources/annotations/label-studio/datapublic/label-studio-config.xml) +- Un glosario explicativo más amplio está disponible aquí: https://docs.google.com/document/d/123B9T2abCEqBaxxOl5c7HBJZRdIMtKDWo6IKHIVil04/edit + +## Documentación relacionada +- Índice de entidades: [../README.md](../README.md) +- Pipeline datapublic: [../../pipelines/datapublic/README.md](../../pipelines/datapublic/README.md) +- Model card de Decision: [../../models/decision-model-card.md](../../models/decision-model-card.md) +- Model card de Flair: [../../models/flair-model-card.md](../../models/flair-model-card.md) diff --git a/docs/es/models/README.md b/docs/es/models/README.md new file mode 100644 index 00000000..3067c6a6 --- /dev/null +++ b/docs/es/models/README.md @@ -0,0 +1,19 @@ +# Modelos +Idioma: [English](../../models/README.md) | **Español** + +Esta sección documenta los modelos individuales utilizados por el backend. + +## Documentación disponible +- Model card del NER de anonymizer: [anonymizer-model-card.md](anonymizer-model-card.md) +- Flair NER: [flair-model-card.md](flair-model-card.md) +- Clasificador de decisiones: [decision-model-card.md](decision-model-card.md) + +## Uso actual en producción +- `flair-anonymizer` usa la model card del NER de anonymizer documentada aquí. +- `full-paragraph` usa el modelo Flair NER y el clasificador de decisiones. + +## Documentación relacionada +- Índice de documentación: [../README.md](../README.md) +- Índice de pipelines: [../pipelines/README.md](../pipelines/README.md) +- Flujo anonymizer: [../pipelines/anonymizer/README.md](../pipelines/anonymizer/README.md) +- Flujo datapublic: [../pipelines/datapublic/README.md](../pipelines/datapublic/README.md) diff --git a/docs/es/models/anonymizer-model-card.md b/docs/es/models/anonymizer-model-card.md new file mode 100644 index 00000000..8ab878d8 --- /dev/null +++ b/docs/es/models/anonymizer-model-card.md @@ -0,0 +1,172 @@ +--- +license: mit +language: +- es +tags: +- flair +- token-classification +- sequence-tagger-model +- anonymization +- judicial-text +datasets: +- ArJuzPCyF10 +metrics: +- precision +- recall +- f1-score +widget: +- text: 1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas. +library_name: flair +pipeline_tag: token-classification +--- + +Idioma: [English](../../models/anonymizer-model-card.md) | **Español** + +# Descripción del modelo + +Este modelo es el componente NER usado por el pipeline de producción `flair-anonymizer`. +Detecta spans que deben anonimizarse en documentos judiciales antes de la desambiguación, la validación manual y el render final del documento. + +Siguiendo las guías de Flair para entrenamiento de NER, el modelo se entrenó sobre [embeddings BETO](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased), una versión en español de BERT, con una arquitectura BiLSTM-CRF. + +Este modelo fue desarrollado por [{ collective.ai }](https://collectiveai.io) como parte del proyecto [AymurAI](https://aymurai.info) de [DataGenero](https://datagenero.org). + +## Usos previstos y limitaciones + +AymurAI busca ayudar a abordar la falta de datos disponibles sobre resoluciones de violencia de género (VG) en América Latina. En el flujo de anonimización, el objetivo inmediato de este modelo es identificar spans sensibles en documentos legales para que puedan ser revisados y reemplazados antes de su uso posterior. + +AymurAI sigue siendo un sistema específico de dominio. Sus capacidades se limitan a la anonimización, recolección y análisis semiautomatizados de datos judiciales, y la salida puede verse afectada por la calidad de la anotación, la heterogeneidad documental, errores de OCR o extracción y la disponibilidad de datos de entrenamiento representativos. + +Este modelo fue entrenado con un dataset cerrado de un juzgado penal argentino. Esa especificidad de dominio mejora el desempeño en el contexto objetivo, pero también implica que el modelo puede no transferir bien a otras jurisdicciones, estilos documentales o culturas jurídicas. + +## Comportamiento en producción + +En producción, este modelo se carga a través de `aymurai.models.flair.core.FlairModel` desde: + +- `resources/pipelines/production/flair-anonymizer/pipeline.json` +- model path: `aymurai/anonymizer-beto-cased-flair` + +Sus predicciones de spans se postprocesan luego con: + +- `aymurai.transforms.anonymization_postprocess.core.AnonymizationEntityCleaner` +- `aymurai.transforms.datetime_formatter.core.DatetimeFormatter` + +Esas predicciones alimentan el resto del flujo de anonimización: + +1. `POST /anonymizer/predict` ejecuta la extracción de spans. +2. `POST /anonymizer/disambiguate` asigna IDs canónicos y metadatos efectivos por label. +3. La revisión manual puede editar labels, `label_policies` y `render_policy`. +4. `POST /anonymizer/anonymize-document` aplica los reemplazos sobre el documento de salida. + +# Uso + +## Cómo usar el modelo en Flair + +Requiere **[Flair](https://github.com/flairNLP/flair/)**. +Instalación: `pip install flair`. + +```python +from flair.data import Sentence +from flair.models import SequenceTagger + +tagger = SequenceTagger.load("aymurai/anonymizer-beto-cased-flair") + +sentence = Sentence( + "1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento " + "de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO " + "MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves " + "agravadas, amenazas simples y agravadas por el uso de armas." +) + +tagger.predict(sentence) + +for entity in sentence.get_spans("ner"): + print(entity) +``` + +Esto produce una salida similar a: + +```text +Span[22:25]: "EZEQUIEL CAMILO MARCONNI" -> PER (0.9541) +Span[27:28]: "11.222.333" -> DNI (1.0) +``` + +## Uso del modelo en un pipeline de AymurAI + +```python +from aymurai.pipeline import AymurAIPipeline + +pipeline = AymurAIPipeline.load("/resources/pipelines/production/flair-anonymizer") + +item = { + "path": "dummy", + "data": { + "doc.text": ( + "Acusado: Ramiro Marrón DNI 34.555.666. " + "Fecha: 17 de noviembre de 2024." + ) + }, +} + +processed = pipeline.preprocess([item]) +processed = pipeline.predict_single(processed[0]) +processed = pipeline.postprocess([processed]) + +print(processed[0]["predictions"]["entities"]) +``` + +# Entidades y métricas + +## Descripción + +Consultá el catálogo de entidades de anonymizer ([en](../../entities/anonymizer/README.md)|[es](../entities/anonymizer/README.md)). + +## Datos + +El modelo fue entrenado con un dataset de 535 resoluciones judiciales de un juzgado penal argentino. + +Dada la naturaleza de los datos (datos personales, características de la denuncia y protección de víctimas), los documentos se mantienen privados. + +## Métricas + +Las siguientes métricas por label provienen de la model card publicada en Hugging Face para esta familia de modelos de anonymizer y deben interpretarse como métricas NER a nivel modelo, no como calidad end-to-end de la anonimización. + +| label | precision | recall | f1-score | +|---|---:|---:|---:| +| `BANCO` | 1.00 | 0.90 | 0.95 | +| `CBU` | 0.92 | 0.92 | 0.92 | +| `CORREO_ELECTRONICO` | 1.00 | 1.00 | 1.00 | +| `CUIJ` | 1.00 | 1.00 | 1.00 | +| `CUIT_CUIL` | 1.00 | 1.00 | 1.00 | +| `DIRECCION` | 0.97 | 0.85 | 0.91 | +| `DNI` | 0.96 | 1.00 | 0.98 | +| `EDAD` | 1.00 | 0.95 | 0.97 | +| `ESTUDIOS` | 1.00 | 1.00 | 1.00 | +| `FECHA` | 1.00 | 0.99 | 1.00 | +| `LINK` | 1.00 | 0.94 | 0.97 | +| `LOC` | 0.99 | 0.72 | 0.83 | +| `MARCA_AUTOMOVIL` | 0.95 | 1.00 | 0.97 | +| `NACIONALIDAD` | 1.00 | 0.94 | 0.97 | +| `NUM_ACTUACION` | 0.84 | 0.96 | 0.90 | +| `NUM_CAJA_AHORRO` | 0.00 | 0.00 | 0.00 | +| `NUM_EXPEDIENTE` | 0.98 | 0.92 | 0.95 | +| `NUM_MATRICULA` | 0.33 | 0.50 | 0.40 | +| `PATENTE_DOMINIO` | 1.00 | 1.00 | 1.00 | +| `PER` | 0.98 | 0.97 | 0.98 | +| `TELEFONO` | 0.97 | 1.00 | 0.99 | +| `TEXTO_ANONIMIZAR` | 0.98 | 0.61 | 0.75 | +| `macro avg` | 0.91 | 0.88 | 0.89 | + +# Cita + +Por favor citá [el siguiente paper](https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view) al utilizar AymurAI: + +```bibtex +@techreport{feldfeber2022, + author = {Feldfeber, Ivana and Quiroga, Yasm{\'i}n Bel{\'e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} +} +``` diff --git a/docs/es/models/decision-model-card.md b/docs/es/models/decision-model-card.md new file mode 100644 index 00000000..22350451 --- /dev/null +++ b/docs/es/models/decision-model-card.md @@ -0,0 +1,217 @@ +--- +license: mit +language: +- es +tags: +- text-classification +- embeddingbag +- binary-classification +- judicial-text +datasets: +- ArJuzPCyF10 +metrics: +- f1 +widget: +- text: 1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas. +library_name: torch +pipeline_tag: text-classification +--- + +Idioma: [English](../../models/decision-model-card.md) | **Español** + +# Descripción del modelo + +Este modelo es el clasificador actual de decisiones a nivel párrafo utilizado en el pipeline de producción `full-paragraph`. +Estima si un párrafo contiene una decisión judicial y, cuando se usa dentro del pipeline de AymurAI, emite una entidad sintética `DECISION` con una subcategoría basada en reglas. + +Este modelo fue desarrollado por [{ collective.ai }](https://collectiveai.io) como parte del proyecto [AymurAI](https://aymurai.info) de [DataGenero](https://datagenero.org). + +## Arquitectura + +El clasificador actual de producción es un modelo compacto de bolsa de palabras con hashing, implementado con `torch.nn.EmbeddingBag`. +Su camino de inferencia está definido en `aymurai/models/decision/binregex.py` y `aymurai/models/decision/embeddingbag.py`. + +A alto nivel, el modelo funciona así: + +1. El texto de entrada se normaliza quitando tildes, pasando a minúsculas y colapsando espacios. +2. El texto se tokeniza por espacios en blanco. +3. Los tokens se hashean con BLAKE2b sobre un vocabulario fijo. +4. Los IDs de tokens se agrupan mediante embeddings promedio con `EmbeddingBag`. +5. Una capa de dropout y una cabeza lineal producen logits binarios (`not decision`, `decision`). +6. Si el score positivo supera el umbral configurado, el pipeline agrega una etiqueta `DECISION` al párrafo. + +Configuración actual del checkpoint cargado por el modelo de producción: + +| parámetro | valor | +|---|---| +| `vocab_size` | `20000` | +| `embed_dim` | `64` | +| `max_tokens` | `128` | +| `dropout` | `0.1` | +| `num_classes` | `2` | +| `batch_size` | `512` | +| `lr` | `0.005` | +| `weight_decay` | `0.001` | +| `epochs` | `50` | + +## Usos previstos y limitaciones + +AymurAI está pensado como una herramienta para abordar la falta de transparencia en el sistema judicial en relación con casos de violencia de género (VG) en América Latina. El objetivo es aumentar los niveles de reporte, construir confianza en el sistema de justicia y mejorar el acceso a la justicia para mujeres y personas LGBTIQ+. AymurAI genera y mantiene datasets anonimizados a partir de sentencias judiciales para comprender la violencia de género y apoyar el diseño de políticas públicas, además de contribuir a campañas de colectivos feministas. + +Las capacidades de AymurAI se limitan a la recolección y análisis semiautomatizados de datos, y sus resultados pueden estar sujetos a limitaciones como la calidad y consistencia de los datos, posibles sesgos del modelo de IA y la disponibilidad de la información. Además, la efectividad de AymurAI para abordar la falta de transparencia del sistema judicial y mejorar el acceso a la justicia también puede depender de otros factores, como el nivel de cooperación de funcionarios judiciales y el contexto cultural y político más amplio. + +Este modelo fue entrenado con un dataset cerrado proveniente de un juzgado penal argentino. Está diseñado para identificar si un párrafo contiene una decisión judicial. El uso de un dataset específico de dominio, proveniente de un juzgado penal argentino, hace que el modelo esté ajustado al contexto jurídico y cultural concreto, lo que permite resultados más precisos. Sin embargo, esto también implica que el modelo puede no ser aplicable o efectivo en otros países o regiones con sistemas jurídicos o normas culturales diferentes. + +## Comportamiento en producción + +Cuando este clasificador se utiliza a través de `DecisionEmbeddingBagBinRegex`, el comportamiento en producción incluye dos reglas adicionales además de la predicción binaria cruda: + +- La clase positiva se emite solo cuando el score de decisión supera el umbral configurado (`0.5` en el pipeline de producción). +- Con `return_only_with_detalle=true`, el clasificador solo agrega una entidad `DECISION` si el párrafo ya contiene una entidad `DETALLE` proveniente de la etapa NER. + +Si un párrafo es clasificado como decisión, el modelo emite una entidad `DECISION` que cubre el texto completo del párrafo. La etiqueta emitida incluye una subcategoría basada en reglas: + +- `hace_lugar` +- `no_hace_lugar` + +Esa subcategoría se asigna mediante postprocesamiento con expresiones regulares sobre el texto del párrafo. + +# Uso + +## Cómo usar el modelo en torch + +```python +import torch + +from aymurai.models.decision.binregex import DecisionEmbeddingBagBinRegex + +model = DecisionEmbeddingBagBinRegex( + model_checkpoint="https://github.com/AymurAI/backend/releases/download/v2.0.0-alpha.1/tiny-embeddingbag.safetensors", + device="cpu", + threshold=0.5, + return_only_with_detalle=False, +) + +text = "1. DECLARAR EXTINGUIDA LA ACCION PENAL en este caso por cumplimiento de la suspension del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas." + +flat_tokens, offsets = model.model_input_from_text(text) +with torch.no_grad(): + logits = model.model(flat_tokens, offsets) + probabilities = logits.softmax(dim=1).cpu().numpy() + +print(probabilities) +print(model.get_subcategory(text)) +``` + +Esto produce una salida similar a: + +```text +[[0.0010057, 0.9989943]] +['hace_lugar'] +``` + +La primera columna es la probabilidad de que el texto no sea una decisión, y la segunda columna es la probabilidad de que el texto sí sea una decisión. + +## Uso del modelo en un pipeline de AymurAI + +El pipeline actual de producción `full-paragraph` incluye este clasificador después de la etapa NER con Flair. + +```python +from aymurai.pipeline import AymurAIPipeline + +pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") + +item = { + "path": "dummy", + "data": { + "doc.text": "1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas." + }, +} + +processed = pipeline.preprocess([item]) +processed = pipeline.predict_single(processed[0]) +processed = pipeline.postprocess([processed]) + +print(processed[0]["predictions"]["entities"]) +``` + +En producción, el clasificador se configura así: + +```json +{ + "aymurai.models.decision.binregex.DecisionEmbeddingBagBinRegex": { + "model_checkpoint": "https://github.com/AymurAI/backend/releases/download/v2.0.0-alpha.1/tiny-embeddingbag.safetensors", + "device": "cpu", + "threshold": 0.5, + "return_only_with_detalle": true + } +} +``` + +# Entidades y métricas + +## Descripción + +Este modelo solo considera la clasificación de párrafos como decisiones o no decisiones. +Cuando la predicción es positiva, el pipeline emite una entidad sintética `DECISION` que cubre todo el párrafo. + +Para la lista completa de entidades usadas por el flujo datapublic, consultá el catálogo de entidades de datapublic ([en](../../entities/datapublic/README.md)|[es](../entities/datapublic/README.md)). + +Para una descripción completa de las entidades consideradas por AymurAI, ver el [Glosario para el dataset con perspectiva de género](https://docs.google.com/document/d/123B9T2abCEqBaxxOl5c7HBJZRdIMtKDWo6IKHIVil04/edit), elaborado por [DataGenero](https://datagenero.org) (solo en español). + +## Datos + +El modelo fue entrenado con un dataset de 1200 resoluciones judiciales de un juzgado penal argentino. + +Dada la naturaleza de los datos (datos personales, características de la denuncia y protección de víctimas), los documentos se mantienen privados. + +### Lista de personas colaboradoras en la anotación + +El dataset fue anotado manualmente por: + +* Diego Scopetta +* Franny Rodriguez Gerzovich ([email](mailto:fraanyrodriguez@gmail.com)|[linkedin](https://www.linkedin.com/in/francescarg)) +* Laura Barreiro +* Matías Sosa +* Maximiliano Sosa +* Patricia Sandoval +* Santiago Bezchinsky ([email](mailto:santibezchinsky@gmail.com)|[linkedin](https://www.linkedin.com/in/santiago-bezchinsky)) +* Zoe Rodriguez Gerzovich + +## Métricas + +Las siguientes métricas provienen de la notebook actual de entrenamiento/evaluación de EmbeddingBag y corresponden a la tarea binaria de clasificación de decisiones (`0 = no decisión`, `1 = decisión`). + +### Split de validación + +| métrica | clase 1 (decisión) | general | +|---|---:|---:| +| precision | 0.774 | - | +| recall | 0.896 | - | +| f1-score | 0.830 | - | +| accuracy | - | 0.962 | +| support | 336 | 3211 | + +### Split de test + +| métrica | clase 1 (decisión) | general | +|---|---:|---:| +| precision | 0.754 | - | +| recall | 0.905 | - | +| f1-score | 0.823 | - | +| accuracy | - | 0.959 | +| support | 336 | 3211 | + +# Cita + +Por favor citá [el siguiente paper](https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view) al utilizar AymurAI: + +```bibtex +@techreport{feldfeber2022, + author = {Feldfeber, Ivana and Quiroga, Yasm{'i}n Bel{'e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} +} +``` diff --git a/docs/es/models/flair-model-card.md b/docs/es/models/flair-model-card.md new file mode 100644 index 00000000..68e2763f --- /dev/null +++ b/docs/es/models/flair-model-card.md @@ -0,0 +1,166 @@ +--- +license: mit +language: +- es +tags: +- flair +- token-classification +- sequence-tagger-model +datasets: +- ArJuzPCyF10 +metrics: +- f1 +widget: +- text: 1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas. +library_name: flair +pipeline_tag: token-classification +--- + +Idioma: [English](../../models/flair-model-card.md) | **Español** + +# Descripción del modelo +

+schema +

+ +Siguiendo las guías de Flair para entrenar un modelo de NER, este modelo se entrenó sobre [embeddings BETO](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased), una versión en español de BERT entrenada sobre un corpus en español, con una arquitectura BiLSTM-CRF. + +Este modelo fue desarrollado por [{ collective.ai }](https://collectiveai.io) como parte del proyecto [AymurAI](https://aymurai.info) de [DataGenero](https://datagenero.org). +Actualmente se usa como componente NER del pipeline de producción `full-paragraph`. + +# Usos previstos y limitaciones +AymurAI está pensado como una herramienta para abordar la falta de transparencia en el sistema judicial en relación con casos de violencia de género (VG) en América Latina. El objetivo es aumentar los niveles de reporte, construir confianza en el sistema de justicia y mejorar el acceso a la justicia para mujeres y personas LGBTIQ+. AymurAI genera y mantiene datasets anonimizados a partir de sentencias judiciales para comprender la violencia de género y apoyar el diseño de políticas públicas, además de contribuir a campañas de colectivos feministas. + +Las capacidades de AymurAI se limitan a la recolección y análisis semiautomatizados de datos, y sus resultados pueden estar sujetos a limitaciones como la calidad y consistencia de los datos, posibles sesgos del modelo de IA y la disponibilidad de la información. Además, la efectividad de AymurAI para abordar la falta de transparencia del sistema judicial y mejorar el acceso a la justicia también puede depender de otros factores, como el nivel de cooperación de funcionarios judiciales y el contexto cultural y político más amplio. + +Este modelo fue entrenado con un dataset cerrado proveniente de un juzgado penal argentino. Está diseñado para identificar y extraer información relevante de sentencias vinculadas con casos de violencia de género. El uso de un dataset específico de dominio, proveniente de un juzgado penal argentino, hace que el modelo esté ajustado al contexto jurídico y cultural concreto, lo que permite resultados más precisos. Sin embargo, esto también implica que el modelo puede no ser aplicable o efectivo en otros países o regiones con sistemas jurídicos o normas culturales diferentes. + +# Uso +## Cómo usar el modelo en Flair + +Requiere **[Flair](https://github.com/flairNLP/flair/)**. +Instalación: `pip install flair` + +```python +from flair.data import Sentence +from flair.models import SequenceTagger + +# load tagger +tagger = SequenceTagger.load("aymurai/flair-ner-spanish-judicial") + +# make example sentence +sentence = Sentence("1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas.") + +# predict NER tags +tagger.predict(sentence) + +# print sentence +print(sentence) + +# print predicted NER spans +print('The following NER tags are found:') +# iterate over entities and print +for entity in sentence.get_spans('ner'): + print(entity) +``` + +Esto produce una salida como la siguiente: + +```text +Span[2:11]: "EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento" → DETALLE (0.5498) +Span[13:18]: "suspensión del proceso a prueba" → OBJETO_DE_LA_RESOLUCION (0.5647) +Span[20:21]: "SOBRESEER" → DETALLE (0.7766) +Span[22:25]: "EZEQUIEL CAMILO MARCONNI" → NOMBRE (0.6454) +Span[35:36]: "lesiones" → CONDUCTA (0.9457) +Span[36:38]: "leves agravadas" → CONDUCTA_DESCRIPCION (0.8818) +Span[39:40]: "amenazas" → CONDUCTA (0.956) +Span[40:48]: "simples y agravadas por el uso de armas" → CONDUCTA_DESCRIPCION (0.6866) +``` + +## Uso del modelo en un pipeline de AymurAI +También podés ejecutar el modelo a través de un pipeline de AymurAI. + +```python +from aymurai.pipeline import AymurAIPipeline + +pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") + +item = { + 'path': 'dummy', + 'data': { + 'doc.text': "1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas." + } +} + +processed = pipeline.preprocess([item]) +processed = pipeline.predict_single(processed[0]) +processed = pipeline.postprocess([processed]) + +print(processed[0]["predictions"]["entities"]) +``` + +# Entidades y métricas +## Descripción +Consultá el catálogo de entidades de datapublic ([en](../../entities/datapublic/README.md)|[es](../entities/datapublic/README.md)). + +Para una descripción completa de las entidades consideradas por AymurAI, ver el [Glosario para el dataset con perspectiva de género](https://docs.google.com/document/d/123B9T2abCEqBaxxOl5c7HBJZRdIMtKDWo6IKHIVil04/edit), elaborado por [DataGenero](https://datagenero.org) (solo en español). + +## Datos +El modelo fue entrenado con un dataset de 1200 resoluciones judiciales de un juzgado penal argentino. + +Dada la naturaleza de los datos (datos personales, características de la denuncia y protección de víctimas), los documentos se mantienen privados. + +### Lista de personas colaboradoras en la anotación +El dataset fue anotado manualmente por: +* Diego Scopetta +* Franny Rodriguez Gerzovich ([email](mailto:fraanyrodriguez@gmail.com)|[linkedin](https://www.linkedin.com/in/francescarg)) +* Laura Barreiro +* Matías Sosa +* Maximiliano Sosa +* Patricia Sandoval +* Santiago Bezchinsky ([email](mailto:santibezchinsky@gmail.com)|[linkedin](https://www.linkedin.com/in/santiago-bezchinsky)) +* Zoe Rodriguez Gerzovich + +## Métricas + +| label | precision | recall | f1-score | +|-----------------------------------------------------|-----------|--------|----------| +| FECHA_DE_NACIMIENTO | 0.98 | 0.99 | 0.99 | +| FECHA_RESOLUCION | 0.95 | 0.98 | 0.96 | +| NACIONALIDAD | 0.94 | 0.98 | 0.96 | +| GENERO | 1.00 | 0.50 | 0.67 | +| HORA_DE_INICIO | 0.98 | 0.92 | 0.95 | +| NOMBRE | 0.94 | 0.95 | 0.95 | +| FRASES_AGRESION | 0.90 | 0.98 | 0.94 | +| HORA_DE_CIERRE | 0.90 | 0.92 | 0.91 | +| NIVEL_INSTRUCCION | 0.85 | 0.94 | 0.90 | +| N_EXPTE_EJE | 0.85 | 0.93 | 0.89 | +| TIPO_DE_RESOLUCION | 0.63 | 0.93 | 0.75 | +| VIOLENCIA_DE_GENERO | 0.49 | 0.59 | 0.54 | +| RELACION_Y_TIPO_ENTRE_ACUSADO/A_Y_DENUNCIANTE | 0.93 | 0.76 | 0.84 | +| HIJOS_HIJAS_EN_COMUN | 0.47 | 0.57 | 0.52 | +| MODALIDAD_DE_LA_VIOLENCIA | 0.57 | 0.56 | 0.57 | +| FECHA_DEL_HECHO | 0.83 | 0.83 | 0.83 | +| CONDUCTA | 0.79 | 0.67 | 0.73 | +| ART_INFRINGIDO | 0.76 | 0.74 | 0.75 | +| DETALLE | 0.53 | 0.37 | 0.43 | +| OBJETO_DE_LA_RESOLUCION | 0.60 | 0.78 | 0.68 | +| CONDUCTA_DESCRIPCION | 0.54 | 0.43 | 0.48 | +| LUGAR_DEL_HECHO | 0.75 | 0.47 | 0.58 | +| EDAD_AL_MOMENTO_DEL_HECHO | 0.50 | 0.20 | 0.29 | +| PERSONA_ACUSADA_NO_DETERMINADA | 0.71 | 0.19 | 0.30 | +| | | | | +| macro avg | 0.77 | 0.72 | 0.73 | + +# Cita +Por favor citá [el siguiente paper](https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view) al utilizar AymurAI: + +```bibtex +@techreport{feldfeber2022, + author = {Feldfeber, Ivana and Quiroga, Yasm'{\i}n Bel'{e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} +} +``` diff --git a/docs/es/pipelines/README.md b/docs/es/pipelines/README.md new file mode 100644 index 00000000..f94559f8 --- /dev/null +++ b/docs/es/pipelines/README.md @@ -0,0 +1,25 @@ +# Pipelines +Idioma: [English](../../pipelines/README.md) | **Español** + +Esta sección documenta los pipelines de producción del backend por flujo. + +## Documentación por flujo +- Anonymizer: [anonymizer/README.md](anonymizer/README.md) +- Datapublic: [datapublic/README.md](datapublic/README.md) + +## Modelos relacionados +- Índice de modelos: [../models/README.md](../models/README.md) +- Model card de Flair NER: [../models/flair-model-card.md](../models/flair-model-card.md) +- Model card de Decision: [../models/decision-model-card.md](../models/decision-model-card.md) + +## Fuentes de configuración en producción +- Config anonymizer: `resources/pipelines/production/flair-anonymizer/pipeline.json` +- Config datapublic: `resources/pipelines/production/full-paragraph/pipeline.json` + +## Mapeo con API +- `POST /anonymizer/predict` -> `flair-anonymizer` +- `POST /datapublic/predict/{document_id}` -> `full-paragraph` + +## Documentación relacionada +- Referencia API: [../api/README.md](../api/README.md) +- Base de datos interna: [../database/README.md](../database/README.md) diff --git a/docs/es/pipelines/anonymizer/README.md b/docs/es/pipelines/anonymizer/README.md new file mode 100644 index 00000000..2d241616 --- /dev/null +++ b/docs/es/pipelines/anonymizer/README.md @@ -0,0 +1,68 @@ +# Pipeline de Anonymizer +Idioma: [English](../../../pipelines/anonymizer/README.md) | **Español** + +Referencia técnica orientada al flujo de anonimización. + +## Alcance +Este flujo extrae entidades del texto judicial y compila documentos anonimizados. + +## Diagrama +![Diagrama del pipeline de anonymizer](../../../pipelines/anonymizer/pipeline.png) + +Fuente editable: [../../../pipelines/anonymizer/pipeline.excalidraw](../../../pipelines/anonymizer/pipeline.excalidraw) + +## Entrypoints de runtime +- `POST /misc/document-extract` +- `POST /anonymizer/predict` +- `POST /anonymizer/disambiguate` +- `POST /anonymizer/validation` +- `POST /anonymizer/anonymize-document` + +## Flujo paso a paso +1. Extracción de texto (`/misc/document-extract`) divide el documento fuente en párrafos normalizados. +2. Predicción (`/anonymizer/predict`) ejecuta NER por párrafo. +3. Desambiguación (`/anonymizer/disambiguate`) asigna IDs canónicos y metadatos efectivos para la desambiguación/anonimización. +4. Revisión manual en UI para editar etiquetas/políticas. +5. Compilación (`/anonymizer/anonymize-document`) aplica reemplazos y exporta `.odt` anonimizado. + +## Componentes técnicos + +### Configuración del pipeline +- Fuente: `resources/pipelines/production/flair-anonymizer/pipeline.json` +- Preprocesamiento: + - `aymurai.models.flair.utils.FlairTextNormalize` +- Modelo: + - `aymurai.models.flair.core.FlairModel` + - base model path: `aymurai/anonymizer-beto-cased-flair` +- Postprocesamiento: + - `aymurai.transforms.anonymization_postprocess.core.AnonymizationEntityCleaner` + - `aymurai.transforms.datetime_formatter.core.DatetimeFormatter` + +### Contratos de API usados por este flujo +- `DocumentInformation` +- `DocumentAnnotations` +- `LabelPolicy` +- `RenderPolicy` +- `EntityAttributes` (en particular: `canonical_entity_id`, `aymurai_label_instance`, `aymurai_disambiguation`, `aymurai_anonymize`) + +### Módulos backend relevantes +- Router: `aymurai/api/endpoints/routers/anonymizer/anonymizer.py` +- Render/anonymize: `aymurai/text/anonymization/doc_anonymizer.py` +- Desambiguación canónica: `aymurai/utils/entity_disambiguation/` + +## Persistencia (DB) +Tablas usadas por este flujo: +- `anonymization_paragraph` +- `anonymization_document` +- `anonymization_document_paragraph` + +## Notas +- Las políticas por label se mergean desde entorno y request. +- `render_policy` controla el comportamiento de sufijos (`auto`, `always`, `never`). + +## Documentación relacionada +- Índice de pipelines: [../README.md](../README.md) +- Entidades de anonymizer: [../../entities/anonymizer/README.md](../../entities/anonymizer/README.md) +- Model card de anonymizer: [../../models/anonymizer-model-card.md](../../models/anonymizer-model-card.md) +- Referencia API: [../../api/README.md](../../api/README.md) +- Base interna: [../../database/README.md](../../database/README.md) diff --git a/docs/es/pipelines/datapublic/README.md b/docs/es/pipelines/datapublic/README.md new file mode 100644 index 00000000..70bc7b36 --- /dev/null +++ b/docs/es/pipelines/datapublic/README.md @@ -0,0 +1,76 @@ +# Pipeline de Datapublic +Idioma: [English](../../../pipelines/datapublic/README.md) | **Español** + +Referencia técnica orientada al flujo de extracción de información del datapublic. + +## Alcance +Este flujo extrae información estructurada a partir de párrafos y soporta persistencia de validación a nivel documento para curación de dataset público. + +## Diagrama +![Diagrama del pipeline de datapublic](../../../pipelines/datapublic/pipeline.png) + +## Entrypoints de runtime +- `POST /misc/document-extract` +- `POST /datapublic/predict/{document_id}` +- `GET /datapublic/validation/document/{document_id}` +- `POST /datapublic/validation/document/{document_id}` + +## Flujo paso a paso +1. Extracción de texto (`/misc/document-extract`) divide el documento fuente en párrafos normalizados. +2. Predicción (`/datapublic/predict/{document_id}`) procesa cada párrafo y devuelve predicciones; la persistencia en caché ocurre solo cuando `use_cache=true` (default). +3. Revisión en UI agrega la salida validada a nivel documento. +4. Los endpoints de validación leen/escriben payload de validación de documento. + +## Componentes técnicos + +### Configuración del pipeline +- Fuente: `resources/pipelines/production/full-paragraph/pipeline.json` +- Preprocesamiento: + - `aymurai.models.flair.utils.FlairTextNormalize` +- Modelos: + - `aymurai.models.flair.core.FlairModel` (`aymurai/flair-ner-spanish-judicial`) + - `aymurai.models.decision.binregex.DecisionEmbeddingBagBinRegex` (`return_only_with_detalle=true` en producción) +- Postprocesamiento: + - `aymurai.transforms.entity_subcategories.regex.RegexSubcategorizer` + - `aymurai.transforms.datetime_formatter.core.DatetimeFormatter` + - `aymurai.transforms.entity_subcategories.sentence_transformer.SentenceTransformerSubcategorizer` (4 instancias configuradas en producción para `CONDUCTA`, `CONDUCTA_DESCRIPCION`, `DETALLE` y `OBJETO_DE_LA_RESOLUCION`) + - `aymurai.transforms.entity_subcategories.article.ArticleSubcategorizer` + +### Algoritmos y procesamiento +- Extracción NER sobre párrafos judiciales. +- Clasificación/filtro de decisiones para relevancia, con gating en producción para que `DECISION` solo se emita cuando ya existe una entidad `DETALLE`. +- Subcategorización rule-based + embeddings. +- Normalización de fecha/hora y mapeo por artículo. + +### Contratos de API usados por este flujo +- `TextRequest` +- `DocumentInformation` +- `DataPublicDocumentAnnotations` (payload libre de validación a nivel documento) + +### Módulos backend relevantes +- Router: `aymurai/api/endpoints/routers/datapublic/datapublic.py` +- Carga de pipeline e inferencia: endpoint de predicción en `aymurai/api/endpoints/routers/datapublic/datapublic.py`. + +## Persistencia (DB) +Tablas usadas por este flujo: +- `datapublic_paragraph` +- `datapublic_document` +- `datapublic_document_paragraph` + +## Notas +- El directorio de pipeline en producción sigue llamándose `full-paragraph`. +- `document_id` es la clave de agrupamiento a nivel documento para asociar predicciones por párrafo y el payload de validación. +- La persistencia de validación es a nivel documento y acepta intencionalmente un objeto JSON libre. +- `GET /datapublic/validation/document/{document_id}` devuelve `404` cuando el documento no existe; `POST` crea o actualiza el payload de validación. +- El router público no monta actualmente `/datapublic/dataset/*`. +- La validación a nivel párrafo aparece en código como lógica legacy comentada y no forma parte del flujo público. + +## Modelos usados por este flujo +- Flair NER: [../../models/flair-model-card.md](../../models/flair-model-card.md) +- Clasificador de decisiones: [../../models/decision-model-card.md](../../models/decision-model-card.md) + +## Documentación relacionada +- Índice de pipelines: [../README.md](../README.md) +- Entidades de datapublic: [../../entities/datapublic/README.md](../../entities/datapublic/README.md) +- Referencia API: [../../api/README.md](../../api/README.md) +- Base interna: [../../database/README.md](../../database/README.md) diff --git a/docs/models/README.md b/docs/models/README.md new file mode 100644 index 00000000..7f2aeafb --- /dev/null +++ b/docs/models/README.md @@ -0,0 +1,19 @@ +# Models +Language: **English** | [Español](../es/models/README.md) + +This section documents the individual models used by the backend. + +## Available model docs +- Anonymizer NER model card: [anonymizer-model-card.md](anonymizer-model-card.md) +- Flair NER: [flair-model-card.md](flair-model-card.md) +- Decision classifier: [decision-model-card.md](decision-model-card.md) + +## Current production usage +- `flair-anonymizer` uses the anonymizer NER model card documented here. +- `full-paragraph` uses the Flair NER model and the decision classifier. + +## Related docs +- Documentation index: [../README.md](../README.md) +- Pipelines index: [../pipelines/README.md](../pipelines/README.md) +- Anonymizer flow: [../pipelines/anonymizer/README.md](../pipelines/anonymizer/README.md) +- Datapublic flow: [../pipelines/datapublic/README.md](../pipelines/datapublic/README.md) diff --git a/docs/models/anonymizer-model-card.md b/docs/models/anonymizer-model-card.md new file mode 100644 index 00000000..53a46813 --- /dev/null +++ b/docs/models/anonymizer-model-card.md @@ -0,0 +1,172 @@ +--- +license: mit +language: +- es +tags: +- flair +- token-classification +- sequence-tagger-model +- anonymization +- judicial-text +datasets: +- ArJuzPCyF10 +metrics: +- precision +- recall +- f1-score +widget: +- text: 1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas. +library_name: flair +pipeline_tag: token-classification +--- + +Language: **English** | [Español](../es/models/anonymizer-model-card.md) + +# Model Description + +This model is the NER component used by the production `flair-anonymizer` pipeline. +It detects spans that should be anonymized in judicial documents before disambiguation, validation, and document rendering. + +Following the Flair guidelines for NER training, the model was trained on top of [BETO embeddings](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased), a Spanish BERT model, with a BiLSTM-CRF architecture. + +This model was developed by [{ collective.ai }](https://collectiveai.io) as part of the [AymurAI](https://aymurai.info) project by [DataGenero](https://datagenero.org). + +## Intended uses & limitations + +AymurAI is intended to help address the lack of available data on gender-based violence (GBV) rulings in Latin America. In the anonymization workflow, the immediate purpose of this model is to identify sensitive spans in legal documents so they can be reviewed and replaced before downstream use. + +AymurAI remains a domain-specific system. Its capabilities are limited to semi-automated anonymization, collection, and analysis of judicial data, and the output may be affected by annotation quality, document heterogeneity, OCR or extraction errors, and the availability of representative training data. + +This model was trained on a closed dataset from an Argentine criminal court. That domain specificity improves performance on the target setting, but it also means the model may not transfer well to other jurisdictions, document styles, or legal cultures. + +## Production behavior + +In production, this model is loaded through `aymurai.models.flair.core.FlairModel` from: + +- `resources/pipelines/production/flair-anonymizer/pipeline.json` +- model path: `aymurai/anonymizer-beto-cased-flair` + +Its raw span predictions are then post-processed by: + +- `aymurai.transforms.anonymization_postprocess.core.AnonymizationEntityCleaner` +- `aymurai.transforms.datetime_formatter.core.DatetimeFormatter` + +Those predictions feed the rest of the anonymization flow: + +1. `POST /anonymizer/predict` runs span extraction. +2. `POST /anonymizer/disambiguate` assigns canonical entity IDs and effective per-label metadata. +3. Manual review may edit labels, `label_policies`, and `render_policy`. +4. `POST /anonymizer/anonymize-document` applies replacements in the output document. + +# Usage + +## How to use the model in Flair + +Requires **[Flair](https://github.com/flairNLP/flair/)**. +Install it with `pip install flair`. + +```python +from flair.data import Sentence +from flair.models import SequenceTagger + +tagger = SequenceTagger.load("aymurai/anonymizer-beto-cased-flair") + +sentence = Sentence( + "1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento " + "de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO " + "MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves " + "agravadas, amenazas simples y agravadas por el uso de armas." +) + +tagger.predict(sentence) + +for entity in sentence.get_spans("ner"): + print(entity) +``` + +This yields output similar to: + +```text +Span[22:25]: "EZEQUIEL CAMILO MARCONNI" -> PER (0.9541) +Span[27:28]: "11.222.333" -> DNI (1.0) +``` + +## Using the model in an AymurAI pipeline + +```python +from aymurai.pipeline import AymurAIPipeline + +pipeline = AymurAIPipeline.load("/resources/pipelines/production/flair-anonymizer") + +item = { + "path": "dummy", + "data": { + "doc.text": ( + "Acusado: Ramiro Marrón DNI 34.555.666. " + "Fecha: 17 de noviembre de 2024." + ) + }, +} + +processed = pipeline.preprocess([item]) +processed = pipeline.predict_single(processed[0]) +processed = pipeline.postprocess([processed]) + +print(processed[0]["predictions"]["entities"]) +``` + +# Entities and metrics + +## Description + +Please refer to the anonymizer entities catalog ([en](../entities/anonymizer/README.md)|[es](../es/entities/anonymizer/README.md)). + +## Data + +The model was trained with a dataset of 535 legal rulings from an Argentine criminal court. + +Due to the nature of the data (personal data, complaint characteristics, and victim protection), the documents are kept private. + +## Metrics + +The following per-label metrics come from the published Hugging Face card for this anonymizer model family and should be interpreted as model-level NER metrics, not end-to-end anonymization quality. + +| label | precision | recall | f1-score | +|---|---:|---:|---:| +| `BANCO` | 1.00 | 0.90 | 0.95 | +| `CBU` | 0.92 | 0.92 | 0.92 | +| `CORREO_ELECTRONICO` | 1.00 | 1.00 | 1.00 | +| `CUIJ` | 1.00 | 1.00 | 1.00 | +| `CUIT_CUIL` | 1.00 | 1.00 | 1.00 | +| `DIRECCION` | 0.97 | 0.85 | 0.91 | +| `DNI` | 0.96 | 1.00 | 0.98 | +| `EDAD` | 1.00 | 0.95 | 0.97 | +| `ESTUDIOS` | 1.00 | 1.00 | 1.00 | +| `FECHA` | 1.00 | 0.99 | 1.00 | +| `LINK` | 1.00 | 0.94 | 0.97 | +| `LOC` | 0.99 | 0.72 | 0.83 | +| `MARCA_AUTOMOVIL` | 0.95 | 1.00 | 0.97 | +| `NACIONALIDAD` | 1.00 | 0.94 | 0.97 | +| `NUM_ACTUACION` | 0.84 | 0.96 | 0.90 | +| `NUM_CAJA_AHORRO` | 0.00 | 0.00 | 0.00 | +| `NUM_EXPEDIENTE` | 0.98 | 0.92 | 0.95 | +| `NUM_MATRICULA` | 0.33 | 0.50 | 0.40 | +| `PATENTE_DOMINIO` | 1.00 | 1.00 | 1.00 | +| `PER` | 0.98 | 0.97 | 0.98 | +| `TELEFONO` | 0.97 | 1.00 | 0.99 | +| `TEXTO_ANONIMIZAR` | 0.98 | 0.61 | 0.75 | +| `macro avg` | 0.91 | 0.88 | 0.89 | + +# Citation + +Please cite [the following paper](https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view) when using AymurAI: + +```bibtex +@techreport{feldfeber2022, + author = {Feldfeber, Ivana and Quiroga, Yasm{\'i}n Bel{\'e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} +} +``` diff --git a/docs/pipeline/assets/ner-schema.png b/docs/models/assets/ner-schema.png similarity index 100% rename from docs/pipeline/assets/ner-schema.png rename to docs/models/assets/ner-schema.png diff --git a/docs/models/decision-model-card.md b/docs/models/decision-model-card.md new file mode 100644 index 00000000..066c2395 --- /dev/null +++ b/docs/models/decision-model-card.md @@ -0,0 +1,217 @@ +--- +license: mit +language: +- es +tags: +- text-classification +- embeddingbag +- binary-classification +- judicial-text +datasets: +- ArJuzPCyF10 +metrics: +- f1 +widget: +- text: 1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas. +library_name: torch +pipeline_tag: text-classification +--- + +Language: **English** | [Español](../es/models/decision-model-card.md) + +# Model Description + +This model is the current paragraph-level decision classifier used in the production `full-paragraph` pipeline. +It estimates whether a paragraph contains a judicial decision and, when used inside the AymurAI pipeline, emits a synthetic `DECISION` entity with a rule-based subclass. + +This model was developed by [{ collective.ai }](https://collectiveai.io) as part of the [AymurAI](https://aymurai.info) project by [DataGenero](https://datagenero.org). + +## Architecture + +The current production classifier is a compact hashed bag-of-words model implemented with `torch.nn.EmbeddingBag`. +Its inference path is defined in `aymurai/models/decision/binregex.py` and `aymurai/models/decision/embeddingbag.py`. + +At a high level, the model works as follows: + +1. Input text is normalized with accent stripping, lowercasing, and whitespace collapsing. +2. Text is tokenized by whitespace. +3. Tokens are hashed with BLAKE2b into a fixed vocabulary. +4. Token IDs are pooled with mean `EmbeddingBag` embeddings. +5. A dropout layer and a linear head produce binary logits (`not decision`, `decision`). +6. If the positive score exceeds the configured threshold, the pipeline appends a `DECISION` label to the paragraph. + +Current checkpoint configuration loaded with the production model: + +| parameter | value | +|---|---| +| `vocab_size` | `20000` | +| `embed_dim` | `64` | +| `max_tokens` | `128` | +| `dropout` | `0.1` | +| `num_classes` | `2` | +| `batch_size` | `512` | +| `lr` | `0.005` | +| `weight_decay` | `0.001` | +| `epochs` | `50` | + +## Intended uses & limitations + +AymurAI is intended to be used as a tool to address the lack of transparency in the judicial system regarding gender-based violence (GBV) cases in Latin America. The goal is to increase report levels, build trust in the justice system, and improve access to justice for women and LGBTIQ+ people. AymurAI will generate and maintain anonymized datasets from legal rulings to understand GBV and support policy making, and also contribute to feminist collectives' campaigns. + +AymurAI capabilities are limited to semi-automated data collection and analysis, and the results may be subject to limitations such as the quality and consistency of the data, potential biases in the AI model, and the availability of the data. Additionally, the effectiveness of AymurAI in addressing the lack of transparency in the judicial system and improving access to justice may also depend on other factors such as the level of cooperation from court officials and the broader cultural and political context. + +This model was trained on a closed dataset from an Argentine criminal court. It is designed to identify whether a paragraph contains a judicial decision. The use of a domain-specific dataset from an Argentine criminal court ensures that the model is tailored to the specific legal and cultural context, allowing for more accurate results. However, it also means that the model may not be applicable or effective in other countries or regions with different legal systems or cultural norms. + +## Production behavior + +When this classifier is used through `DecisionEmbeddingBagBinRegex`, the production behavior includes two additional rules beyond the raw binary prediction: + +- The positive class is emitted only when the decision score is above the configured threshold (`0.5` in the production pipeline). +- With `return_only_with_detalle=true`, the classifier only appends a `DECISION` entity if the paragraph already contains a `DETALLE` entity from the NER stage. + +If a paragraph is classified as a decision, the model emits a `DECISION` entity covering the full paragraph text. The emitted label includes a rule-based subclass: + +- `hace_lugar` +- `no_hace_lugar` + +That subclass is assigned with regex-based post-processing over the paragraph text. + +# Usage + +## How to use the model in torch + +```python +import torch + +from aymurai.models.decision.binregex import DecisionEmbeddingBagBinRegex + +model = DecisionEmbeddingBagBinRegex( + model_checkpoint="https://github.com/AymurAI/backend/releases/download/v2.0.0-alpha.1/tiny-embeddingbag.safetensors", + device="cpu", + threshold=0.5, + return_only_with_detalle=False, +) + +text = "1. DECLARAR EXTINGUIDA LA ACCION PENAL en este caso por cumplimiento de la suspension del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas." + +flat_tokens, offsets = model.model_input_from_text(text) +with torch.no_grad(): + logits = model.model(flat_tokens, offsets) + probabilities = logits.softmax(dim=1).cpu().numpy() + +print(probabilities) +print(model.get_subcategory(text)) +``` + +This yields output similar to: + +```text +[[0.0010057, 0.9989943]] +['hace_lugar'] +``` + +The first column is the probability of the text not being a decision, and the second column is the probability of the text being a decision. + +## Using the model in an AymurAI pipeline + +The current production `full-paragraph` pipeline includes this classifier after the Flair NER stage. + +```python +from aymurai.pipeline import AymurAIPipeline + +pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") + +item = { + "path": "dummy", + "data": { + "doc.text": "1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas." + }, +} + +processed = pipeline.preprocess([item]) +processed = pipeline.predict_single(processed[0]) +processed = pipeline.postprocess([processed]) + +print(processed[0]["predictions"]["entities"]) +``` + +In production, the classifier is configured as: + +```json +{ + "aymurai.models.decision.binregex.DecisionEmbeddingBagBinRegex": { + "model_checkpoint": "https://github.com/AymurAI/backend/releases/download/v2.0.0-alpha.1/tiny-embeddingbag.safetensors", + "device": "cpu", + "threshold": 0.5, + "return_only_with_detalle": true + } +} +``` + +# Entities and metrics + +## Description + +This model only considers the classification of paragraphs as decisions or non-decisions. +When the prediction is positive, the pipeline emits a synthetic `DECISION` entity that spans the full paragraph. + +For the complete list of entities used by the datapublic flow, please refer to the datapublic entities catalog ([en](../entities/datapublic/README.md)|[es](../es/entities/datapublic/README.md)). + +For a complete description of the entities considered by AymurAI, refer to the [Glossary for the Dataset with gender perspective](https://docs.google.com/document/d/123B9T2abCEqBaxxOl5c7HBJZRdIMtKDWo6IKHIVil04/edit) written by [DataGenero](https://datagenero.org) (Spanish only). + +## Data + +The model was trained with a dataset of 1200 legal rulings from an Argentine criminal court. + +Due to the nature of the data (personal data, complaint characteristics, and victim protection), the documents are kept private. + +### List of annotation contributors + +The dataset was manually annotated by: + +* Diego Scopetta +* Franny Rodriguez Gerzovich ([email](mailto:fraanyrodriguez@gmail.com)|[linkedin](https://www.linkedin.com/in/francescarg)) +* Laura Barreiro +* Matías Sosa +* Maximiliano Sosa +* Patricia Sandoval +* Santiago Bezchinsky ([email](mailto:santibezchinsky@gmail.com)|[linkedin](https://www.linkedin.com/in/santiago-bezchinsky)) +* Zoe Rodriguez Gerzovich + +## Metrics + +The following metrics were obtained from the current EmbeddingBag training/evaluation notebook and correspond to the binary decision classification task (`0 = not decision`, `1 = decision`). + +### Validation split + +| metric | class 1 (decision) | overall | +|---|---:|---:| +| precision | 0.774 | - | +| recall | 0.896 | - | +| f1-score | 0.830 | - | +| accuracy | - | 0.962 | +| support | 336 | 3211 | + +### Test split + +| metric | class 1 (decision) | overall | +|---|---:|---:| +| precision | 0.754 | - | +| recall | 0.905 | - | +| f1-score | 0.823 | - | +| accuracy | - | 0.959 | +| support | 336 | 3211 | + +# Citation + +Please cite [the following paper](https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view) when using AymurAI: + +```bibtex +@techreport{feldfeber2022, + author = {Feldfeber, Ivana and Quiroga, Yasm{\'i}n Bel{\'e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} +} +``` diff --git a/docs/pipeline/flair-model-card.md b/docs/models/flair-model-card.md similarity index 69% rename from docs/pipeline/flair-model-card.md rename to docs/models/flair-model-card.md index 15fd493d..8b9ceddc 100644 --- a/docs/pipeline/flair-model-card.md +++ b/docs/models/flair-model-card.md @@ -16,23 +16,24 @@ library_name: flair pipeline_tag: token-classification --- +Language: **English** | [Español](../es/models/flair-model-card.md) + # Model Description

schema

-Following the FLAIR guidelines for training a NER model, we trained a model on top of [BETO embeddings](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased) (a spanish version of BERT trained in a spanish corpus) and a BiLSTM-CRF architecture. +Following the Flair guidelines for training a NER model, we trained this model on top of [BETO embeddings](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased), a Spanish version of BERT trained on a Spanish corpus, with a BiLSTM-CRF architecture. This model was developed by [{ collective.ai }](https://collectiveai.io) as part of the [AymurAI](https://aymurai.info) project by [DataGenero](https://datagenero.org). - - +It is currently used as the NER component of the production `full-paragraph` pipeline. # Intended uses & limitations AymurAI is intended to be used as a tool to address the lack of transparency in the judicial system regarding gender-based violence (GBV) cases in Latin America. The goal is to increase report levels, build trust in the justice system, and improve access to justice for women and LGBTIQ+ people. AymurAI will generate and maintain anonymized datasets from legal rulings to understand GBV and support policy making, and also contribute to feminist collectives' campaigns. -AymurAI is still a prototype and is only being implemented in Argentina and Mexico. Its capabilities are limited to semi-automated data collection and analysis, and the results may be subject to limitations such as the quality and consistency of the data, potential biases in the AI model, and the availability of the data. Additionally, the effectiveness of AymurAI in addressing the lack of transparency in the judicial system and improving access to justice may also depend on other factors such as the level of cooperation from court officials and the broader cultural and political context. +AymurAI capabilities are limited to semi-automated data collection and analysis, and the results may be subject to limitations such as the quality and consistency of the data, potential biases in the AI model, and the availability of the data. Additionally, the effectiveness of AymurAI in addressing the lack of transparency in the judicial system and improving access to justice may also depend on other factors such as the level of cooperation from court officials and the broader cultural and political context. -This model was trained with a closed dataset from an Argentine criminal court. It's is designed to identify and extract relevant information from court sentences related to GBV cases. The use of a domain specific dataset from an Argentine criminal court ensures that the model is tailored to the specific legal and cultural context, allowing for more accurate results. However, it also means that the model may not be applicable or effective in other countries or regions with different legal systems or cultural norms. +This model was trained on a closed dataset from an Argentine criminal court. It is designed to identify and extract relevant information from court sentences related to GBV cases. The use of a domain-specific dataset from an Argentine criminal court ensures that the model is tailored to the specific legal and cultural context, allowing for more accurate results. However, it also means that the model may not be applicable or effective in other countries or regions with different legal systems or cultural norms. # Usage ## How to use the model in Flair @@ -76,36 +77,11 @@ Span[39:40]: "amenazas" → CONDUCTA (0.956) Span[40:48]: "simples y agravadas por el uso de armas" → CONDUCTA_DESCRIPCION (0.6866) ``` -## Using the model in aymurai pipeline -You also can run the model directly in the aymurai pipeline. +## Using the model in an AymurAI pipeline +You can also run the model through an AymurAI pipeline. ```python from aymurai.pipeline import AymurAIPipeline -from aymurai.models.flair.utils import FlairTextNormalize -from aymurai.models.flair.core import FlairModel - -config = { - "preprocess": [ - [ - "aymurai.models.flair.utils.FlairTextNormalize", - {} - ] - ], - "models": [ - [ - "aymurai.models.flair.core.FlairModel", - { - "basepath": "https://drive.google.com/uc?id=1QJYkegv_P3d27FyRLabIUiw9154ur9-I&confirm=true", - "split_doc": false, - "device": "cpu" - } - ] - ], - "postprocess": [], - "use_cache": false -} -} - pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") @@ -116,7 +92,11 @@ item = { } } -pipeline.predict([item]) +processed = pipeline.preprocess([item]) +processed = pipeline.predict_single(processed[0]) +processed = pipeline.postprocess([processed]) + +print(processed[0]["predictions"]["entities"]) ``` @@ -124,24 +104,26 @@ pipeline.predict([item]) # Entities and metrics ## Description -Please refer to the entities' description table ([en](../data/en/entities-table.md)|[es](../data/es/entities-table.md)). +Please refer to the datapublic entities catalog ([en](../entities/datapublic/README.md)|[es](../es/entities/datapublic/README.md)). For a complete description about entities considered by AymurAI, refer to the [Glossary for the Dataset with gender perspective](https://docs.google.com/document/d/123B9T2abCEqBaxxOl5c7HBJZRdIMtKDWo6IKHIVil04/edit) written by [DataGenero](https://datagenero.org) (spanish only) ## Data The model was trained with a dataset of 1200 legal rulings from an Argentine criminal court. -Due to the nature of the data (personal data, complaint characteristics and victim protection) the documents are kept private. +Due to the nature of the data (personal data, complaint characteristics, and victim protection), the documents are kept private. + ### List of annotation contributors The dataset was manually annotated by: * Diego Scopetta -* Franny Rodriguez Gerzovich ([email](fraanyrodriguez@gmail.com)|[linkedin](https://www.linkedin.com/in/francescarg)) +* Franny Rodriguez Gerzovich ([email](mailto:fraanyrodriguez@gmail.com)|[linkedin](https://www.linkedin.com/in/francescarg)) * Laura Barreiro * Matías Sosa * Maximiliano Sosa * Patricia Sandoval -* Santiago Bezchinsky ([email](santibezchinsky@gmail.com)|[linkedin](https://www.linkedin.com/in/santiago-bezchinsky)) +* Santiago Bezchinsky ([email](mailto:santibezchinsky@gmail.com)|[linkedin](https://www.linkedin.com/in/santiago-bezchinsky)) * Zoe Rodriguez Gerzovich + ## Metrics | label | precision | recall | f1-score | @@ -179,10 +161,10 @@ Please cite [the following paper](https://drive.google.com/file/d/1P-hW0JKXWZ44F ```bibtex @techreport{feldfeber2022, - author = "Feldfeber, Ivana and Quiroga, Yasmín Belén and Guevara, Clarissa and Ciolfi Felice, Marianela", - title = "Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico", - institution = "DataGenero", - year = "2022", - url = "https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view" + author = {Feldfeber, Ivana and Quiroga, Yasm\'{\i}n Bel\'{e}n and Guevara, Clarissa and Ciolfi Felice, Marianela}, + title = {Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico}, + institution = {DataGenero}, + year = {2022}, + url = {https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view} } ``` diff --git a/docs/pipeline/README.md b/docs/pipeline/README.md deleted file mode 100644 index 22514bf3..00000000 --- a/docs/pipeline/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Pipeline -

-schema -

- -The AymurAI pipeline is written in a modular way, each step is independent and can be replaced by another one. This allows us to easily change the models or the way in which the information is processed. -It's made up of three main steps: preprocessing, inference and postprocessing. -The preprocessing step is in charge of dividing the court rulings into paragraphs and normalizing their text. The inference step is in charge of extracting the information from the paragraphs. Finally, the postprocessing step is in charge of filtering out irrelevant decisions and formatting the text. -# Preprocessing -The flow of information in the system starts with a pre-processing step for the court rulings, where the documents are divided into paragraphs and their text is normalized. This normalization process involves the unification of multiple spaces and the removal of accents and/or special characters . -In this way, the paragraphs become the minimum unit of analysis, and predictions are generated in parallel and independently from one another. - - -# Inference -

-schema -

- -The fundamental piece of the AymurAI backend is the AI models. They are in charge of extracting information from court rulings. -For their development, we use the following work frameworks: -The NER model was developed using [Flair](https://github.com/flairNLP/flair), a Python library created by Humboldt - University of Berlin focused on natural language processing models. Flair is based on PyTorch, one of the main artificial intelligence frameworks, and allows integration with Transformers, another of the main libraries of natural language processing models. -For the decision model we use PyTorch natively. - -Each paragraph is processed by these models, and the results are combined. - -Check out the [model cards](#model-cards) for more information about the models, and the [model training](#model-training-notebooks) section for more information about how the models were trained. - -# Postprocessing -Once the inference is made, the predictions are postprocessed in order to obtain the final results. -This process involves: -* Subcategorization extraction or classification -* Filtering out irrelevant decisions -* Text formatting - -## Subcategorization -* Regex -Regular expressions allow us to identify some subcategories whose texts tend to be repeated following very marked patterns. - -* Sentence Similarity -Entities with many subcategories, and less well represented, are identified using sentence similarity. The Universal Sentence Encoder Multilingual QA model, published by Google, allows us to encode the text identified as belonging to certain categories in order to then calculate a semantic similarity score with respect to each possible subcategory and thus generate a ranking of candidates, ordered from highest to lowest similarity. - - -## Text Formating -The NER model extract non-structured information from the text. Dates and times are reformatted to a standard format. - -## Filters -Many of the decisions that are extracted by the models are not relevant to the user. Relevant decisions are those that contain a specific type of information, such as presence of other relevant entities (e.i DETALLE) - -# Models -## Model cards -* [Flair NER Spanish Judicial](./flair-model-card.md) -* [Decision Text Classification](./decision-model-card.md) - -# Model Training Notebooks -* [Flair NER Spanish Judicial](../../notebooks/experiments/ner/flair/) -* [Decision Text Classification](../../notebooks/experiments/decision/) diff --git a/docs/pipeline/assets/decision-confusion-matrix.png b/docs/pipeline/assets/decision-confusion-matrix.png deleted file mode 100644 index a88291fe5890ed66542e08275fcdc043dbd39414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15037 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
3@fCH3wEf{s=|%_0O>Y#c8ljg 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 P!q0RMANQm3@J;^%toS)o diff --git a/docs/pipeline/assets/inference-example.png b/docs/pipeline/assets/inference-example.png deleted file mode 100644 index bb6000dd923725a7bd8d20eca9e6ed4f375e1b38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80766 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⁢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 diff --git a/docs/pipeline/assets/postprocessing-example.png b/docs/pipeline/assets/postprocessing-example.png deleted file mode 100644 index 9bd51d84e583f92f74250eeb0971d41aeec910f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64041 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 diff --git a/docs/pipeline/decision-model-card.md b/docs/pipeline/decision-model-card.md deleted file mode 100644 index e1e37bb3..00000000 --- a/docs/pipeline/decision-model-card.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -license: mit -language: -- es -tags: -- text-classification -datasets: -- ArJuzPCyF10 -metrics: -- f1 -widget: -- text: 1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas. -library_name: torch -pipeline_tag: text-classification ---- - -# Model Description -

-schema -

- -This model was developed by [{ collective.ai }](https://collectiveai.io) as part of the [AymurAI](https://aymurai.info) project by [DataGenero](https://datagenero.org). - - - -# Intended uses & limitations -AymurAI is intended to be used as a tool to address the lack of transparency in the judicial system regarding gender-based violence (GBV) cases in Latin America. The goal is to increase report levels, build trust in the justice system, and improve access to justice for women and LGBTIQ+ people. AymurAI will generate and maintain anonymized datasets from legal rulings to understand GBV and support policy making, and also contribute to feminist collectives' campaigns. - -AymurAI is still a prototype and is only being implemented in Argentina and Mexico. Its capabilities are limited to semi-automated data collection and analysis, and the results may be subject to limitations such as the quality and consistency of the data, potential biases in the AI model, and the availability of the data. Additionally, the effectiveness of AymurAI in addressing the lack of transparency in the judicial system and improving access to justice may also depend on other factors such as the level of cooperation from court officials and the broader cultural and political context. - -This model was trained with a closed dataset from an Argentine criminal court. It's is designed to identify and extract relevant information from court sentences related to GBV cases. The use of a domain specific dataset from an Argentine criminal court ensures that the model is tailored to the specific legal and cultural context, allowing for more accurate results. However, it also means that the model may not be applicable or effective in other countries or regions with different legal systems or cultural norms. - -# Usage -## How to use the model in torch - - -```python - -from aymurai.models.decision.binregex import DecisionConv1dBinRegex - -model = DecisionConv1dBinRegex( - tokenizer_path="https://drive.google.com/uc?id=1eljQOinpObdfBREIKxVnC5Y2g_sbhPHT&confirm=true", - model_checkpoint="https://drive.google.com/uc?id=19_YmBJnO06iS0qW8ak0zl0EIsJYin8kQ&confirm=true", - device="cpu", -) - -text = "1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas." - -input_ids = model.tokenizer.encode_batch([text]) -input_ids.shape - -# The model return the logsoftmax of the probabilities of the classes -probabilities = model.model(input_ids).exp().detach().numpy() -probabilities - - -``` - -This yields the following output: -``` -array([[2.2457261e-05, 9.9997759e-01]], dtype=float32) -``` -The first column is the probability of the text not being a decision, and the second column is the probability of the text being a decision. For this example, the model predicts that the text is a decision with a probability of 99.99%. - -## Using the model in aymurai pipeline -You also can run the model directly in the aymurai pipeline. - -```python -from aymurai.pipeline import AymurAIPipeline -from aymurai.models.flair.utils import FlairTextNormalize -from aymurai.models.decision.binregex import DecisionConv1dBinRegex - -config = { - "preprocess": [ - [ - "aymurai.models.flair.utils.FlairTextNormalize", - {} - ] - ], - "models": [ - - [ - "aymurai.models.decision.binregex.DecisionConv1dBinRegex", - { - "tokenizer_path": "https://drive.google.com/uc?id=1eljQOinpObdfBREIKxVnC5Y2g_sbhPHT&confirm=true", - "model_checkpoint": "https://drive.google.com/uc?id=19_YmBJnO06iS0qW8ak0zl0EIsJYin8kQ&confirm=true", - "device": "cpu", - "threshold": 0.5, - "return_only_with_detalle": true - } - ] - ], - "postprocess": [ - ], - "use_cache": false -} - - -pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") - -item = { - 'path': 'dummy', - 'data': { - 'doc.text': "1. DECLARAR EXTINGUIDA LA ACCIÓN PENAL en este caso por cumplimiento de la suspensión del proceso a prueba, y SOBRESEER a EZEQUIEL CAMILO MARCONNI, DNI 11.222.333, en orden a los delitos de lesiones leves agravadas, amenazas simples y agravadas por el uso de armas." - } -} - -pipeline.predict([item]) - - -``` - - - - -# Entities and metrics -## Description -This model only considers the classification of paragraphs being decisions or not. - -For the complete list of entities, please refer to the entities' description table ([en](../data/en/entities-table.md)|[es](../data/es/entities-table.md)). - -For a complete description about entities considered by AymurAI, refer to the [Glossary for the Dataset with gender perspective](https://docs.google.com/document/d/123B9T2abCEqBaxxOl5c7HBJZRdIMtKDWo6IKHIVil04/edit) written by [DataGenero](https://datagenero.org) (spanish only) - - -## Data -The model was trained with a dataset of 1200 legal rulings from an Argentine criminal court. - -Due to the nature of the data (personal data, complaint characteristics and victim protection) the documents are kept private. -### List of annotation contributors -The dataset was manually annotated by: -* Diego Scopetta -* Franny Rodriguez Gerzovich ([email](fraanyrodriguez@gmail.com)|[linkedin](https://www.linkedin.com/in/francescarg)) -* Laura Barreiro -* Matías Sosa -* Maximiliano Sosa -* Patricia Sandoval -* Santiago Bezchinsky ([email](santibezchinsky@gmail.com)|[linkedin](https://www.linkedin.com/in/santiago-bezchinsky)) -* Zoe Rodriguez Gerzovich -## Metrics - -

-schema -

- -Class 0: Not a decision -Class 1: Decision - - - -## Citation -Please cite [the following paper](https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view) when using AymurAI: - -```bibtex -@techreport{feldfeber2022, - author = "Feldfeber, Ivana and Quiroga, Yasmín Belén and Guevara, Clarissa and Ciolfi Felice, Marianela", - title = "Feminisms in Artificial Intelligence: Automation Tools towards a Feminist Judiciary Reform in Argentina and Mexico", - institution = "DataGenero", - year = "2022", - url = "https://drive.google.com/file/d/1P-hW0JKXWZ44Fn94fDVIxQRTExkK6m4Y/view" -} -``` diff --git a/docs/pipelines/README.md b/docs/pipelines/README.md new file mode 100644 index 00000000..b09731d6 --- /dev/null +++ b/docs/pipelines/README.md @@ -0,0 +1,25 @@ +# Pipelines +Language: **English** | [Español](../es/pipelines/README.md) + +This section documents AymurAI backend production pipelines by workflow. + +## Flow docs +- Anonymizer: [anonymizer/README.md](anonymizer/README.md) +- Datapublic: [datapublic/README.md](datapublic/README.md) + +## Related model docs +- Models index: [../models/README.md](../models/README.md) +- Flair NER model card: [../models/flair-model-card.md](../models/flair-model-card.md) +- Decision model card: [../models/decision-model-card.md](../models/decision-model-card.md) + +## Production pipeline sources +- Anonymizer config: `resources/pipelines/production/flair-anonymizer/pipeline.json` +- Datapublic config: `resources/pipelines/production/full-paragraph/pipeline.json` + +## API mapping +- `POST /anonymizer/predict` -> `flair-anonymizer` +- `POST /datapublic/predict/{document_id}` -> `full-paragraph` + +## Related docs +- API reference: [../api/README.md](../api/README.md) +- Internal database: [../database/README.md](../database/README.md) diff --git a/docs/pipelines/anonymizer/README.md b/docs/pipelines/anonymizer/README.md new file mode 100644 index 00000000..11e864e7 --- /dev/null +++ b/docs/pipelines/anonymizer/README.md @@ -0,0 +1,68 @@ +# Anonymizer Pipeline +Language: **English** | [Español](../../es/pipelines/anonymizer/README.md) + +Workflow-oriented technical reference for anonymization. + +## Scope +This flow extracts entities from judicial text and compiles anonymized output documents. + +## Diagram +![Anonymizer pipeline diagram](pipeline.png) + +Editable source: [pipeline.excalidraw](pipeline.excalidraw) + +## Runtime entrypoints +- `POST /misc/document-extract` +- `POST /anonymizer/predict` +- `POST /anonymizer/disambiguate` +- `POST /anonymizer/validation` +- `POST /anonymizer/anonymize-document` + +## Step-by-step flow +1. Text extraction (`/misc/document-extract`) splits source document into normalized paragraphs. +2. Prediction (`/anonymizer/predict`) runs NER on each paragraph. +3. Disambiguation (`/anonymizer/disambiguate`) assigns canonical entity IDs and effective anonymization/disambiguation metadata. +4. Manual review in UI edits labels and optional policies. +5. Compilation (`/anonymizer/anonymize-document`) applies replacements and exports anonymized `.odt`. + +## Technical components + +### Pipeline configuration +- Source: `resources/pipelines/production/flair-anonymizer/pipeline.json` +- Preprocess: + - `aymurai.models.flair.utils.FlairTextNormalize` +- Model: + - `aymurai.models.flair.core.FlairModel` + - base model path: `aymurai/anonymizer-beto-cased-flair` +- Postprocess: + - `aymurai.transforms.anonymization_postprocess.core.AnonymizationEntityCleaner` + - `aymurai.transforms.datetime_formatter.core.DatetimeFormatter` + +### API contracts used by this flow +- `DocumentInformation` +- `DocumentAnnotations` +- `LabelPolicy` +- `RenderPolicy` +- `EntityAttributes` (notably: `canonical_entity_id`, `aymurai_label_instance`, `aymurai_disambiguation`, `aymurai_anonymize`) + +### Core backend modules +- Router: `aymurai/api/endpoints/routers/anonymizer/anonymizer.py` +- Rendering: `aymurai/text/anonymization/doc_anonymizer.py` +- Canonical entity mapping: `aymurai/utils/entity_disambiguation/` + +## Persistence (DB) +Tables touched by this flow: +- `anonymization_paragraph` +- `anonymization_document` +- `anonymization_document_paragraph` + +## Notes +- Label policies are merged from environment and request payload. +- Render policy controls suffix behavior (`auto`, `always`, `never`) during replacement. + +## Related docs +- Pipelines index: [../README.md](../README.md) +- Anonymizer entities: [../../entities/anonymizer/README.md](../../entities/anonymizer/README.md) +- Anonymizer model card: [../../models/anonymizer-model-card.md](../../models/anonymizer-model-card.md) +- API reference: [../../api/README.md](../../api/README.md) +- Internal database: [../../database/README.md](../../database/README.md) diff --git a/docs/pipelines/anonymizer/pipeline.excalidraw b/docs/pipelines/anonymizer/pipeline.excalidraw new file mode 100644 index 00000000..f7da8a73 --- /dev/null +++ b/docs/pipelines/anonymizer/pipeline.excalidraw @@ -0,0 +1,1748 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "M3_9cSeevpvgvuGOJmtLu", + "type": "rectangle", + "x": 341.0753968253973, + "y": -946.5918028903099, + "width": 215.66666666666663, + "height": 280.0447761194029, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0P", + "roundness": { + "type": 3 + }, + "seed": 630737269, + "version": 231, + "versionNonce": 857423573, + "isDeleted": false, + "boundElements": [ + { + "id": "EOtxLYz_c5dBLBZTsf6bP", + "type": "arrow" + } + ], + "updated": 1764875361089, + "link": null, + "locked": false + }, + { + "id": "e0VYlO1HBUE6Y61hMmjQS", + "type": "line", + "x": 384.5306207059946, + "y": -906.3554844823498, + "width": 94.95771144278605, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0Q", + "roundness": { + "type": 2 + }, + "seed": 924406485, + "version": 197, + "versionNonce": 1258245525, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 94.95771144278605, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "zz4q6sGFYLSQEGm4_Z_pZ", + "type": "line", + "x": 380.15053275977357, + "y": -879.2079349266717, + "width": 48.28358208955214, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0R", + "roundness": { + "type": 2 + }, + "seed": 2054303797, + "version": 300, + "versionNonce": 2102055669, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.28358208955214, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "S9t8FU85SfKbOpC8RR_gM", + "type": "line", + "x": 380.6970945304854, + "y": -849.4353562329334, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0S", + "roundness": { + "type": 2 + }, + "seed": 227041685, + "version": 330, + "versionNonce": 1522445397, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "DL-iPq6d9xxkjVSnghr83", + "type": "text", + "x": 394.20929106726203, + "y": -619.8728974176731, + "width": 82.81063842773438, + "height": 120.7089552238806, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0T", + "roundness": null, + "seed": 898721525, + "version": 217, + "versionNonce": 1900882722, + "isDeleted": false, + "boundElements": [], + "updated": 1772204466428, + "link": null, + "locked": false, + "text": ".docx\n.pdf\n.odt", + "fontSize": 32.189054726368155, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": ".docx\n.pdf\n.odt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "eb7-zegRUYIJqYobn0GTg", + "type": "line", + "x": 379.7022624970393, + "y": -821.0544894574741, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0U", + "roundness": { + "type": 2 + }, + "seed": 1964163157, + "version": 228, + "versionNonce": 2143640341, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "76_13jw7FNEf9CD3vN4K7", + "type": "line", + "x": 386.86680332119363, + "y": -794.1556155836837, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0V", + "roundness": { + "type": 2 + }, + "seed": 1642196405, + "version": 262, + "versionNonce": 418590837, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "7bpvHSILjOEGoxBVuVvmL", + "type": "line", + "x": 387.21934633300566, + "y": -768.6496839272999, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0W", + "roundness": { + "type": 2 + }, + "seed": 992243477, + "version": 368, + "versionNonce": 1820285397, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "hunvhsGsscndVNFWE5PbJ", + "type": "line", + "x": 386.2245142995596, + "y": -740.2688171518405, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0X", + "roundness": { + "type": 2 + }, + "seed": 1062192245, + "version": 266, + "versionNonce": 1132418869, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "T8wwR2IwhMmplgY6InC65", + "type": "line", + "x": 393.3890551237139, + "y": -713.3699432780502, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "baV9NX1y69MAosZs6e-CT" + ], + "frameId": null, + "index": "b0Y", + "roundness": { + "type": 2 + }, + "seed": 1110011349, + "version": 300, + "versionNonce": 676898965, + "isDeleted": false, + "boundElements": [], + "updated": 1764875361089, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "EOtxLYz_c5dBLBZTsf6bP", + "type": "arrow", + "x": 560.3079384765334, + "y": -801.8590832623923, + "width": 311.10872819013355, + "height": 3.3450992498784444, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0Z", + "roundness": { + "type": 2 + }, + "seed": 46590773, + "version": 868, + "versionNonce": 416388706, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "AQOsTPNecUVIP5dcYG-CL" + } + ], + "updated": 1772207740751, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 311.10872819013355, + 3.3450992498784444 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "M3_9cSeevpvgvuGOJmtLu", + "focus": 0.024877658056798795, + "gap": 5.80129262390335 + }, + "endBinding": { + "elementId": "vWBP3oXNVHZGwRqV71dye", + "focus": 0.16647678467610177, + "gap": 26.273809523809405 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "AQOsTPNecUVIP5dcYG-CL", + "type": "text", + "x": 633.3624170125181, + "y": -825.1557337445369, + "width": 164.99977111816406, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0a", + "roundness": null, + "seed": 1099807893, + "version": 58, + "versionNonce": 587560482, + "isDeleted": false, + "boundElements": [], + "updated": 1772204827339, + "link": null, + "locked": false, + "text": "/misc/document-\nextract", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "EOtxLYz_c5dBLBZTsf6bP", + "originalText": "/misc/document-extract", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "vWBP3oXNVHZGwRqV71dye", + "type": "text", + "x": 897.6904761904764, + "y": -891.8596681096677, + "width": 536.5656565656566, + "height": 232.7272727272728, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0b", + "roundness": null, + "seed": 372466165, + "version": 440, + "versionNonce": 1496857790, + "isDeleted": false, + "boundElements": [ + { + "id": "EOtxLYz_c5dBLBZTsf6bP", + "type": "arrow" + } + ], + "updated": 1772204827339, + "link": null, + "locked": false, + "text": "{\n \"document\": [\n \"R., MATIAS EZEQUIEL SOBRE\n128 1 párr\",\n ...\n ],\n \"document_id\": \n}", + "fontSize": 23.27272727272728, + "fontFamily": 8, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{\n \"document\": [\n \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n ...\n ],\n \"document_id\": \n}", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "uIFK9kOYNJE4xD48qvRxR", + "type": "text", + "x": 1973.8949799899103, + "y": -527.8682896843161, + "width": 351.6396893419871, + "height": 74.26086756682336, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0f", + "roundness": null, + "seed": 1570520949, + "version": 508, + "versionNonce": 1517826942, + "isDeleted": false, + "boundElements": [], + "updated": 1772207749195, + "link": null, + "locked": false, + "text": "Per-paragraph prediction\n(DocumentInformation)", + "fontSize": 29.704347026729355, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Per-paragraph prediction\n(DocumentInformation)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "cPvNsqjYFBYhf1dvqCfUM", + "type": "text", + "x": 5111.82726940021, + "y": -661.9813153131714, + "width": 335.071044921875, + "height": 80.4726368159204, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0o", + "roundness": null, + "seed": 183537621, + "version": 526, + "versionNonce": 1023679678, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "text": "Anonymized document\n.odt", + "fontSize": 32.189054726368155, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Anonymized document\n.odt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "H45OT-nnyrYBIdX_fHnB-", + "type": "rectangle", + "x": 5171.529458527813, + "y": -988.7002207858083, + "width": 215.66666666666663, + "height": 280.0447761194029, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0o8", + "roundness": { + "type": 3 + }, + "seed": 1543168597, + "version": 491, + "versionNonce": 1377686782, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false + }, + { + "id": "HkLFBBIeoT2NmdHolQogn", + "type": "line", + "x": 5214.984682408411, + "y": -948.4639023778478, + "width": 94.95771144278605, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0oG", + "roundness": { + "type": 2 + }, + "seed": 997576629, + "version": 455, + "versionNonce": 1138132286, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 94.95771144278605, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "dP23wQQAJGa8EbTko6TLk", + "type": "line", + "x": 5210.604594462189, + "y": -921.31635282217, + "width": 48.28358208955214, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0oV", + "roundness": { + "type": 2 + }, + "seed": 1005326613, + "version": 558, + "versionNonce": 328150398, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.28358208955214, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "AzAxanw4nR0bo9R-OtYUW", + "type": "line", + "x": 5211.1511562329015, + "y": -891.5437741284319, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0ol", + "roundness": { + "type": 2 + }, + "seed": 1255351925, + "version": 588, + "versionNonce": 1854459326, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "FZMsE2ftxJlqlXy5y1Kot", + "type": "line", + "x": 5210.156324199455, + "y": -863.1629073529726, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0p", + "roundness": { + "type": 2 + }, + "seed": 710528309, + "version": 485, + "versionNonce": 837051902, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "c5LayQF2_U6-IqmzQigBO", + "type": "line", + "x": 5217.320865023609, + "y": -836.2640334791819, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0pG", + "roundness": { + "type": 2 + }, + "seed": 2020892309, + "version": 520, + "versionNonce": 1856173630, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "CZP0a-w2xzB-aIeBIdTfj", + "type": "line", + "x": 5217.673408035422, + "y": -810.758101822798, + "width": 131.97512437810926, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0pV", + "roundness": { + "type": 2 + }, + "seed": 1005962229, + "version": 626, + "versionNonce": 1839562366, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 131.97512437810926, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "YGpIXuZRgXAXrbeyTS72v", + "type": "line", + "x": 5216.678576001976, + "y": -782.3772350473388, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0q", + "roundness": { + "type": 2 + }, + "seed": 1222868309, + "version": 524, + "versionNonce": 866975422, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "ABZSKgAIt_rm_gemO06m9", + "type": "line", + "x": 5223.8431168261295, + "y": -755.4783611735489, + "width": 128.75621890547262, + "height": 1.6094527363184077, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mnNZnb2eX5SBS_s6ArNSy" + ], + "frameId": null, + "index": "b0r", + "roundness": { + "type": 2 + }, + "seed": 1452477109, + "version": 558, + "versionNonce": 89814782, + "isDeleted": false, + "boundElements": [], + "updated": 1772207001336, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 128.75621890547262, + 1.6094527363184077 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "hotXOq7-8juh39kLJem4p", + "type": "arrow", + "x": 2454.591377430666, + "y": -806.9942535766841, + "width": 374.23286489749944, + "height": 2.3331459911660204, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0u", + "roundness": { + "type": 2 + }, + "seed": 755633851, + "version": 550, + "versionNonce": 1019750946, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "XSjKuWf82l8iev5nfszlU" + } + ], + "updated": 1772206699138, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 374.23286489749944, + 2.3331459911660204 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "XSjKuWf82l8iev5nfszlU", + "type": "text", + "x": 2517.6379398842982, + "y": -818.327680581101, + "width": 248.13973999023438, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0uV", + "roundness": null, + "seed": 193864190, + "version": 43, + "versionNonce": 360400482, + "isDeleted": false, + "boundElements": null, + "updated": 1772206699138, + "link": null, + "locked": false, + "text": "/anonymizer/disambiguate", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hotXOq7-8juh39kLJem4p", + "originalText": "/anonymizer/disambiguate", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "gXsFYxTEKv14jMJGLJI8O", + "type": "arrow", + "x": 1452.931012958739, + "y": -798.134476023053, + "width": 403.8000183105464, + "height": 5.908527289127619, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0v", + "roundness": { + "type": 2 + }, + "seed": 1974610101, + "version": 1591, + "versionNonce": 620553314, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Q4IQFcmT8i1SnTx5DuzTS" + } + ], + "updated": 1772205508184, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 195.91698365823413, + -2.048276293639333 + ], + [ + 403.8000183105464, + -5.908527289127619 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "Q4IQFcmT8i1SnTx5DuzTS", + "type": "text", + "x": 1547.6477860456841, + "y": -810.1827523166924, + "width": 202.40042114257812, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0w", + "roundness": null, + "seed": 2134198805, + "version": 73, + "versionNonce": 1627097790, + "isDeleted": false, + "boundElements": [], + "updated": 1772205508184, + "link": null, + "locked": false, + "text": "N x /anonymizer/predict", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "gXsFYxTEKv14jMJGLJI8O", + "originalText": "N x /anonymizer/predict", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "jFnC8PtgWHvjc9weWuFWX", + "type": "text", + "x": 326.69940776522253, + "y": -1119.4404761904757, + "width": 558.84033203125, + "height": 76.99999999999986, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0z", + "roundness": null, + "seed": 29900859, + "version": 88, + "versionNonce": 181080958, + "isDeleted": false, + "boundElements": [], + "updated": 1772204432143, + "link": null, + "locked": false, + "text": "Anonymizer pipeline", + "fontSize": 61.59999999999989, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Anonymizer pipeline", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "LUVMyylE6hgpMsgfw7aao", + "type": "rectangle", + "x": 1888.8814913275708, + "y": -991.508812100596, + "width": 521.6666666666664, + "height": 428.88888888888886, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b13", + "roundness": { + "type": 3 + }, + "seed": 1127296034, + "version": 420, + "versionNonce": 1975633314, + "isDeleted": false, + "boundElements": [], + "updated": 1772204924667, + "link": null, + "locked": false + }, + { + "id": "1ulzr8Q7P6FFgdWlFr0cp", + "type": "text", + "x": 1911.6383476101225, + "y": -918.7310343228181, + "width": 476.1529541015625, + "height": 283.3333333333333, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b14", + "roundness": null, + "seed": 238770146, + "version": 239, + "versionNonce": 7850686, + "isDeleted": false, + "boundElements": [], + "updated": 1772206121905, + "link": null, + "locked": false, + "text": "{\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n ...\n }\n }\n ]\n}", + "fontSize": 16.19047619047619, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "{\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n ...\n }\n }\n ]\n}", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "vrSceZivuOFwwm43eU0mr", + "type": "text", + "x": 924.6722658756239, + "y": -606.9260633331838, + "width": 482.6020771953618, + "height": 43.38079023612419, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b15", + "roundness": null, + "seed": 1891961378, + "version": 472, + "versionNonce": 550795810, + "isDeleted": false, + "boundElements": [], + "updated": 1772207740751, + "link": null, + "locked": false, + "text": "Document extraction output", + "fontSize": 34.70463218889935, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Document extraction output", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "GNTtv7Qdsx0aEF6kxRZ5a", + "type": "text", + "x": 2736.8880249454437, + "y": -1160.814903248222, + "width": 1100.7685546875, + "height": 832.0238095238095, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b17", + "roundness": null, + "seed": 882291582, + "version": 555, + "versionNonce": 362426494, + "isDeleted": false, + "boundElements": [], + "updated": 1772207723129, + "link": null, + "locked": false, + "text": "{\n \"data\": [\n {\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n \"aymurai_label_instance\": 1,\n 'aymurai_disambiguation': 'fuzzy',\n 'aymurai_anonymize': True,\n 'canonical_entity_id': \n }\n }\n ]\n },\n ...\n ],\n \"label_policies\": {\n \"PER\": {\"anonymize\": True, \"disambiguation\": \"fuzzy\", \"use_subclass_when_available\": True},\n \"FECHA\": {\"anonymize\": False, \"disambiguation\": \"none\", \"use_subclass_when_available\": False}\n },\n \"render_policy\": {\n \"suffix_mode\": \"auto\",\n \"suffix_threshold\": 1\n }", + "fontSize": 22.952380952380953, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{\n \"data\": [\n {\n \"document\": \"R., MATIAS EZEQUIEL SOBRE 128 1 párr\",\n \"labels\": [\n {\n \"text\": \"R., MATIAS EZEQUIEL\",\n \"start_char\": 0,\n \"end_char\": 23,\n \"attrs\": {\n \"aymurai_label\": \"PER\",\n \"aymurai_label_instance\": 1,\n 'aymurai_disambiguation': 'fuzzy',\n 'aymurai_anonymize': True,\n 'canonical_entity_id': \n }\n }\n ]\n },\n ...\n ],\n \"label_policies\": {\n \"PER\": {\"anonymize\": True, \"disambiguation\": \"fuzzy\", \"use_subclass_when_available\": True},\n \"FECHA\": {\"anonymize\": False, \"disambiguation\": \"none\", \"use_subclass_when_available\": False}\n },\n \"render_policy\": {\n \"suffix_mode\": \"auto\",\n \"suffix_threshold\": 1\n }", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "GpgRoWvkFU8pY8qyanTEz", + "type": "text", + "x": 2971.7865914541294, + "y": -294.843926983973, + "width": 630.971421670128, + "height": 41.08319145660856, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b18", + "roundness": null, + "seed": 440634338, + "version": 526, + "versionNonce": 60141218, + "isDeleted": false, + "boundElements": [], + "updated": 1772207760618, + "link": null, + "locked": false, + "text": "Review payload (DocumentAnnotations)", + "fontSize": 32.86655316528685, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Review payload (DocumentAnnotations)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "t1CVOK0LIbIN3hNMusjFo", + "type": "arrow", + "x": 3486.838089935834, + "y": -810.7442535766841, + "width": 374.23286489749944, + "height": 2.3331459911660204, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b19", + "roundness": { + "type": 2 + }, + "seed": 1603482402, + "version": 639, + "versionNonce": 2029670818, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "9SlkoqV3LCMNC6VOzAR4J" + } + ], + "updated": 1772206766396, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 374.23286489749944, + 2.3331459911660204 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "9SlkoqV3LCMNC6VOzAR4J", + "type": "text", + "x": 3609.451244288067, + "y": -828.7443472477678, + "width": 122.33988952636719, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1A", + "roundness": null, + "seed": 2126806754, + "version": 83, + "versionNonce": 1522294242, + "isDeleted": false, + "boundElements": [], + "updated": 1772206766396, + "link": null, + "locked": false, + "text": "Review in UI", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "t1CVOK0LIbIN3hNMusjFo", + "originalText": "Review in UI", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "1g5mwsMmOsivD02csjkTB", + "type": "text", + "x": 3879.9078976523183, + "y": -604.1107637238993, + "width": 809.6706988450118, + "height": 36.97264967907313, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1C", + "roundness": null, + "seed": 306784958, + "version": 693, + "versionNonce": 1588957730, + "isDeleted": false, + "boundElements": [], + "updated": 1772207711810, + "link": null, + "locked": false, + "text": "Manual review: edit labels, label policies and render policy", + "fontSize": 29.5781197432585, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Manual review: edit labels, label policies and render policy", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lhZO6MPRRTWy3V55I0vZM", + "type": "arrow", + "x": 4705.8560612283145, + "y": -807.5294042874617, + "width": 403.8000183105464, + "height": 5.908527289127619, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1G", + "roundness": { + "type": 2 + }, + "seed": 280036245, + "version": 1662, + "versionNonce": 1583584226, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "GHolMdYEXJBOSjiLEJF7i" + } + ], + "updated": 1772207526341, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 195.91698365823413, + -2.048276293639333 + ], + [ + 403.8000183105464, + -5.908527289127619 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "GHolMdYEXJBOSjiLEJF7i", + "type": "text", + "x": 4769.7727702283455, + "y": -819.577680581101, + "width": 264.00054931640625, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1H", + "roundness": null, + "seed": 1331022069, + "version": 58, + "versionNonce": 451123582, + "isDeleted": false, + "boundElements": [], + "updated": 1772206985239, + "link": null, + "locked": false, + "text": "/anonymizer/anonymize-document", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lhZO6MPRRTWy3V55I0vZM", + "originalText": "/anonymizer/anonymize-document", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "oRmHjBYwEzoCu3wVivfxA", + "type": "image", + "x": 3870.418043822792, + "y": -974.1722733335696, + "width": 828.6504065040658, + "height": 351.8703703703709, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1M", + "roundness": null, + "seed": 22438654, + "version": 258, + "versionNonce": 1815649534, + "isDeleted": false, + "boundElements": [], + "updated": 1772207295559, + "link": null, + "locked": false, + "status": "pending", + "fileId": "ac6c81549182e851ebe5aa58dd0fba44c4ab568b", + "scale": [ + 1, + 1 + ], + "crop": { + "x": 0, + "y": 0, + "width": 1440, + "height": 611.4681527412586, + "naturalWidth": 1440, + "naturalHeight": 820 + } + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": { + "ac6c81549182e851ebe5aa58dd0fba44c4ab568b": { + "mimeType": "image/png", + "id": "ac6c81549182e851ebe5aa58dd0fba44c4ab568b", + "dataURL": "data:image/png;base64,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", + "created": 1772207236966, + "lastRetrieved": 1772207236966 + } + } +} \ No newline at end of file diff --git a/docs/pipelines/anonymizer/pipeline.png b/docs/pipelines/anonymizer/pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..61ce789ec4b08fa347044d3a3d4f2d9866d90a14 GIT binary patch literal 601943 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

1o*FsLL`6+q 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

dw+jI80 z(K2!)E$3Z!%V}>U0ZuFSTBIj%l5Ks;_V2V*R2*$TP1hz2146BTnN|wu;+;^G-n!z8 zJ_2&b8DZ@BM(2nGws0=63;$KNT~)2PKj~O4VSId1-pPMSKDj9}eM4>N>|9+)VHSDU zz1ahKyHavfRj?V@3K(fVZnNH&*899VvAM~xMRLHill-{}gveb!IhdDrE(T8ycig6S zoLvP1h^#I>Ep4B;!%~;(_4ig|PBDR*-UULab0gh z4H9}H8*;{0$B%76&jJH}o*AHv1snG$`Hl)#DNtzI^e!MSQuJXR`<|Wn#zdI?@xhbn zQAnG3gR(Td8ztGUB~p%*{Ll+pUcCP`d!HD)*AHE{1BiWZ1X65e8f>mf z2cT`yphGB2mzMQqtWhKG8~;At&30TR7`SWkk`I`O1U3bs+3tTKs;YF}k~MIf(GX{g z_oQRFxaZ&@k3C5kfE{-Hz3(UyY|WZO=?!jw^Lh{G-Mc?fnD=mC7#IU{1*5Y&9K~bV zElK$g42*_D7)8kD1w^4&&k5f@{Q7Ec?xoq`uTyXaph4u7=S=d&(GLNTQmv|*n$!va z;y)+)M>#&X@z_kcM3R4B3ar7MNSvr{uw}K(^6_f>_FIdtrQW5GPp^#aIV^qictPp;eeB;qmPGe!?c4p8L3Otbd2Qnu3u=Ba;LCuXYzkSK54her$;H=nFmT-zkHm~!qPVkHS zP~VrE`rr5ZX_c0chWDcQ|N2H!93fxRW%P|SMYDhYJ2`uEi$sntK}RNa{e7j{ivxy1mc5)l!A?gN@- zgLeUO0tWYG<6{)?Ejkb^J19#>LY4ik%yzH?BTEzh@%SPOe^g}2Cj)4=6CLcguMTrw zCFizDpoPNHVJUA()kGOOg$XZ!e=dB4Yj8(SO4*B=fp!zx(zDvc_>HxD`k*Rq;5}ZVI7P zZi`!P*#9Q_-JukRYVOqvs+Ptk;&@)sfduznF~DchZ)A;XA#)%+WCpx)cw1sQ>&x%f ztww`Hj)EmyB~)j_acDPiuT73mTf?Rd$1d3X=kC3}g)8)VEfnbV++h=Q{k|W^L&P9N z6UaW;hsZ!q`dWFy`KV=kJHILP$s~u`+gqC z@_C)D#0EKA(Mm~41sS3+W30RHLeXz1tQ7t9%WOe*8X!T&R_?O&Qwf(tVXli5~$8moV|2@uC|Te{6A z4Kz`trQ3HFQf2F6f2&sp8A*ymTxc0Zh%5Y?*Jazz9(-)vs_!EcXD4mlY#W>xrzgl& zY7!$kvim~4$0~}7G>+_!H1k7b06Y0)e*G1XO9NnamO?1l$wp7xAbttMx22Efn^iFf z_=%SU6-LU)BI>z|si`IQMq1QYfpV|}Qgdx^8634FaJ^E;sc28Out?o*nT4K8?rd

LbU(3!01B+0d^BmwBv=c8eHx3sd%< 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 hZf*Eu4&pz|LiN1d Date: Mon, 2 Mar 2026 18:30:18 +0000 Subject: [PATCH 090/101] =?UTF-8?q?=F0=9F=9A=9A=20Rename=20full-paragraph?= =?UTF-8?q?=20pipeline=20to=20datapublic=20across=20code=20and=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.es.md | 2 +- README.md | 2 +- aymurai/api/endpoints/routers/datapublic/datapublic.py | 2 +- aymurai/api/main.py | 2 +- docs/entities/README.md | 2 +- docs/entities/datapublic/README.md | 2 +- docs/es/entities/README.md | 2 +- docs/es/entities/datapublic/README.md | 2 +- docs/es/models/README.md | 2 +- docs/es/models/decision-model-card.md | 6 +++--- docs/es/models/flair-model-card.md | 4 ++-- docs/es/pipelines/README.md | 4 ++-- docs/es/pipelines/datapublic/README.md | 4 ++-- docs/models/README.md | 2 +- docs/models/decision-model-card.md | 6 +++--- docs/models/flair-model-card.md | 4 ++-- docs/pipelines/README.md | 4 ++-- docs/pipelines/datapublic/README.md | 4 ++-- .../production/{full-paragraph => datapublic}/pipeline.json | 0 19 files changed, 28 insertions(+), 28 deletions(-) rename resources/pipelines/production/{full-paragraph => datapublic}/pipeline.json (100%) diff --git a/README.es.md b/README.es.md index eb8acd07..90e939c0 100644 --- a/README.es.md +++ b/README.es.md @@ -84,7 +84,7 @@ make api-logs - URI de DB por defecto: `sqlite:////resources/cache/sqlite/database.db` - Configs de pipeline de producción: - `resources/pipelines/production/flair-anonymizer/pipeline.json` - - `resources/pipelines/production/full-paragraph/pipeline.json` + - `resources/pipelines/production/datapublic/pipeline.json` ## Endpoints públicos principales - `GET /server/healthcheck` diff --git a/README.md b/README.md index a19c2224..fae091de 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ make api-logs - Default DB URI: `sqlite:////resources/cache/sqlite/database.db` - Production pipeline configs: - `resources/pipelines/production/flair-anonymizer/pipeline.json` - - `resources/pipelines/production/full-paragraph/pipeline.json` + - `resources/pipelines/production/datapublic/pipeline.json` ## Main Public Endpoints - `GET /server/healthcheck` diff --git a/aymurai/api/endpoints/routers/datapublic/datapublic.py b/aymurai/api/endpoints/routers/datapublic/datapublic.py index 62af6761..91e1f0d5 100644 --- a/aymurai/api/endpoints/routers/datapublic/datapublic.py +++ b/aymurai/api/endpoints/routers/datapublic/datapublic.py @@ -62,7 +62,7 @@ async def predict_over_text( logger.info("Running prediction") item = [{"path": "empty", "data": {"doc.text": text_request.text}}] pipeline = load_pipeline( - os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "full-paragraph") + os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "datapublic") ) with pipeline_lock: diff --git a/aymurai/api/main.py b/aymurai/api/main.py index d814a3f6..b4ef93d6 100644 --- a/aymurai/api/main.py +++ b/aymurai/api/main.py @@ -109,5 +109,5 @@ def healthcheck(): os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "flair-anonymizer") ) AymurAIPipeline.load( - os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "full-paragraph") + os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "datapublic") ) diff --git a/docs/entities/README.md b/docs/entities/README.md index 5f516a31..e12055f7 100644 --- a/docs/entities/README.md +++ b/docs/entities/README.md @@ -8,7 +8,7 @@ This section documents the entity catalogs used by AymurAI workflows. - Anonymizer: [anonymizer/README.md](anonymizer/README.md) ## Notes -- `datapublic` entities describe the public-data extraction taxonomy used by the production `full-paragraph` pipeline. +- `datapublic` entities describe the public-data extraction taxonomy used by the production `datapublic` pipeline. - `anonymizer` entities describe the labels currently used by the anonymization flow for disambiguation and replacement. ## Related docs diff --git a/docs/entities/datapublic/README.md b/docs/entities/datapublic/README.md index 99079a7e..578e1c30 100644 --- a/docs/entities/datapublic/README.md +++ b/docs/entities/datapublic/README.md @@ -4,7 +4,7 @@ Language: **English** | [Español](../../es/entities/datapublic/README.md) Catalog of entities used by the datapublic extraction flow. ## Scope -These entities correspond to the taxonomy extracted from judicial rulings by the production `full-paragraph` pipeline. They are used across the Flair NER model, decision post-processing, validation UI, and related dataset tooling. +These entities correspond to the taxonomy extracted from judicial rulings by the production `datapublic` pipeline. They are used across the Flair NER model, decision post-processing, validation UI, and related dataset tooling. ## Entities | Entity | Description | diff --git a/docs/es/entities/README.md b/docs/es/entities/README.md index 5152c66a..59fb1247 100644 --- a/docs/es/entities/README.md +++ b/docs/es/entities/README.md @@ -8,7 +8,7 @@ Esta sección documenta los catálogos de entidades utilizados por los distintos - Anonymizer: [anonymizer/README.md](anonymizer/README.md) ## Notas -- Las entidades de `datapublic` describen la taxonomía de extracción de datos públicos usada por el pipeline de producción `full-paragraph`. +- Las entidades de `datapublic` describen la taxonomía de extracción de datos públicos usada por el pipeline de producción `datapublic`. - Las entidades de `anonymizer` describen las labels usadas actualmente por el flujo de anonimización para desambiguación y reemplazo. ## Documentación relacionada diff --git a/docs/es/entities/datapublic/README.md b/docs/es/entities/datapublic/README.md index 9b9c1c91..e645344f 100644 --- a/docs/es/entities/datapublic/README.md +++ b/docs/es/entities/datapublic/README.md @@ -4,7 +4,7 @@ Idioma: [English](../../../entities/datapublic/README.md) | **Español** Catálogo de entidades utilizadas por el flujo de extracción de datapublic. ## Alcance -Estas entidades corresponden a la taxonomía extraída de resoluciones judiciales por el pipeline de producción `full-paragraph`. Se usan a lo largo del modelo Flair NER, el postprocesamiento de decisiones, la UI de validación y el tooling relacionado con el dataset. +Estas entidades corresponden a la taxonomía extraída de resoluciones judiciales por el pipeline de producción `datapublic`. Se usan a lo largo del modelo Flair NER, el postprocesamiento de decisiones, la UI de validación y el tooling relacionado con el dataset. ## Entidades | Entidad | Descripción | diff --git a/docs/es/models/README.md b/docs/es/models/README.md index 3067c6a6..a843aee4 100644 --- a/docs/es/models/README.md +++ b/docs/es/models/README.md @@ -10,7 +10,7 @@ Esta sección documenta los modelos individuales utilizados por el backend. ## Uso actual en producción - `flair-anonymizer` usa la model card del NER de anonymizer documentada aquí. -- `full-paragraph` usa el modelo Flair NER y el clasificador de decisiones. +- `datapublic` usa el modelo Flair NER y el clasificador de decisiones. ## Documentación relacionada - Índice de documentación: [../README.md](../README.md) diff --git a/docs/es/models/decision-model-card.md b/docs/es/models/decision-model-card.md index 22350451..0735c450 100644 --- a/docs/es/models/decision-model-card.md +++ b/docs/es/models/decision-model-card.md @@ -21,7 +21,7 @@ Idioma: [English](../../models/decision-model-card.md) | **Español** # Descripción del modelo -Este modelo es el clasificador actual de decisiones a nivel párrafo utilizado en el pipeline de producción `full-paragraph`. +Este modelo es el clasificador actual de decisiones a nivel párrafo utilizado en el pipeline de producción `datapublic`. Estima si un párrafo contiene una decisión judicial y, cuando se usa dentro del pipeline de AymurAI, emite una entidad sintética `DECISION` con una subcategoría basada en reglas. Este modelo fue desarrollado por [{ collective.ai }](https://collectiveai.io) como parte del proyecto [AymurAI](https://aymurai.info) de [DataGenero](https://datagenero.org). @@ -114,12 +114,12 @@ La primera columna es la probabilidad de que el texto no sea una decisión, y la ## Uso del modelo en un pipeline de AymurAI -El pipeline actual de producción `full-paragraph` incluye este clasificador después de la etapa NER con Flair. +El pipeline actual de producción `datapublic` incluye este clasificador después de la etapa NER con Flair. ```python from aymurai.pipeline import AymurAIPipeline -pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") +pipeline = AymurAIPipeline.load("/resources/pipelines/production/datapublic") item = { "path": "dummy", diff --git a/docs/es/models/flair-model-card.md b/docs/es/models/flair-model-card.md index 68e2763f..5efa7a81 100644 --- a/docs/es/models/flair-model-card.md +++ b/docs/es/models/flair-model-card.md @@ -26,7 +26,7 @@ Idioma: [English](../../models/flair-model-card.md) | **Español** Siguiendo las guías de Flair para entrenar un modelo de NER, este modelo se entrenó sobre [embeddings BETO](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased), una versión en español de BERT entrenada sobre un corpus en español, con una arquitectura BiLSTM-CRF. Este modelo fue desarrollado por [{ collective.ai }](https://collectiveai.io) como parte del proyecto [AymurAI](https://aymurai.info) de [DataGenero](https://datagenero.org). -Actualmente se usa como componente NER del pipeline de producción `full-paragraph`. +Actualmente se usa como componente NER del pipeline de producción `datapublic`. # Usos previstos y limitaciones AymurAI está pensado como una herramienta para abordar la falta de transparencia en el sistema judicial en relación con casos de violencia de género (VG) en América Latina. El objetivo es aumentar los niveles de reporte, construir confianza en el sistema de justicia y mejorar el acceso a la justicia para mujeres y personas LGBTIQ+. AymurAI genera y mantiene datasets anonimizados a partir de sentencias judiciales para comprender la violencia de género y apoyar el diseño de políticas públicas, además de contribuir a campañas de colectivos feministas. @@ -83,7 +83,7 @@ También podés ejecutar el modelo a través de un pipeline de AymurAI. ```python from aymurai.pipeline import AymurAIPipeline -pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") +pipeline = AymurAIPipeline.load("/resources/pipelines/production/datapublic") item = { 'path': 'dummy', diff --git a/docs/es/pipelines/README.md b/docs/es/pipelines/README.md index f94559f8..c6ff29fa 100644 --- a/docs/es/pipelines/README.md +++ b/docs/es/pipelines/README.md @@ -14,11 +14,11 @@ Esta sección documenta los pipelines de producción del backend por flujo. ## Fuentes de configuración en producción - Config anonymizer: `resources/pipelines/production/flair-anonymizer/pipeline.json` -- Config datapublic: `resources/pipelines/production/full-paragraph/pipeline.json` +- Config datapublic: `resources/pipelines/production/datapublic/pipeline.json` ## Mapeo con API - `POST /anonymizer/predict` -> `flair-anonymizer` -- `POST /datapublic/predict/{document_id}` -> `full-paragraph` +- `POST /datapublic/predict/{document_id}` -> `datapublic` ## Documentación relacionada - Referencia API: [../api/README.md](../api/README.md) diff --git a/docs/es/pipelines/datapublic/README.md b/docs/es/pipelines/datapublic/README.md index 70bc7b36..aec3291c 100644 --- a/docs/es/pipelines/datapublic/README.md +++ b/docs/es/pipelines/datapublic/README.md @@ -24,7 +24,7 @@ Este flujo extrae información estructurada a partir de párrafos y soporta pers ## Componentes técnicos ### Configuración del pipeline -- Fuente: `resources/pipelines/production/full-paragraph/pipeline.json` +- Fuente: `resources/pipelines/production/datapublic/pipeline.json` - Preprocesamiento: - `aymurai.models.flair.utils.FlairTextNormalize` - Modelos: @@ -58,7 +58,7 @@ Tablas usadas por este flujo: - `datapublic_document_paragraph` ## Notas -- El directorio de pipeline en producción sigue llamándose `full-paragraph`. +- El directorio de pipeline en producción sigue llamándose `datapublic`. - `document_id` es la clave de agrupamiento a nivel documento para asociar predicciones por párrafo y el payload de validación. - La persistencia de validación es a nivel documento y acepta intencionalmente un objeto JSON libre. - `GET /datapublic/validation/document/{document_id}` devuelve `404` cuando el documento no existe; `POST` crea o actualiza el payload de validación. diff --git a/docs/models/README.md b/docs/models/README.md index 7f2aeafb..8893a1e4 100644 --- a/docs/models/README.md +++ b/docs/models/README.md @@ -10,7 +10,7 @@ This section documents the individual models used by the backend. ## Current production usage - `flair-anonymizer` uses the anonymizer NER model card documented here. -- `full-paragraph` uses the Flair NER model and the decision classifier. +- `datapublic` uses the Flair NER model and the decision classifier. ## Related docs - Documentation index: [../README.md](../README.md) diff --git a/docs/models/decision-model-card.md b/docs/models/decision-model-card.md index 066c2395..715f4354 100644 --- a/docs/models/decision-model-card.md +++ b/docs/models/decision-model-card.md @@ -21,7 +21,7 @@ Language: **English** | [Español](../es/models/decision-model-card.md) # Model Description -This model is the current paragraph-level decision classifier used in the production `full-paragraph` pipeline. +This model is the current paragraph-level decision classifier used in the production `datapublic` pipeline. It estimates whether a paragraph contains a judicial decision and, when used inside the AymurAI pipeline, emits a synthetic `DECISION` entity with a rule-based subclass. This model was developed by [{ collective.ai }](https://collectiveai.io) as part of the [AymurAI](https://aymurai.info) project by [DataGenero](https://datagenero.org). @@ -114,12 +114,12 @@ The first column is the probability of the text not being a decision, and the se ## Using the model in an AymurAI pipeline -The current production `full-paragraph` pipeline includes this classifier after the Flair NER stage. +The current production `datapublic` pipeline includes this classifier after the Flair NER stage. ```python from aymurai.pipeline import AymurAIPipeline -pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") +pipeline = AymurAIPipeline.load("/resources/pipelines/production/datapublic") item = { "path": "dummy", diff --git a/docs/models/flair-model-card.md b/docs/models/flair-model-card.md index 8b9ceddc..54143211 100644 --- a/docs/models/flair-model-card.md +++ b/docs/models/flair-model-card.md @@ -26,7 +26,7 @@ Language: **English** | [Español](../es/models/flair-model-card.md) Following the Flair guidelines for training a NER model, we trained this model on top of [BETO embeddings](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased), a Spanish version of BERT trained on a Spanish corpus, with a BiLSTM-CRF architecture. This model was developed by [{ collective.ai }](https://collectiveai.io) as part of the [AymurAI](https://aymurai.info) project by [DataGenero](https://datagenero.org). -It is currently used as the NER component of the production `full-paragraph` pipeline. +It is currently used as the NER component of the production `datapublic` pipeline. # Intended uses & limitations AymurAI is intended to be used as a tool to address the lack of transparency in the judicial system regarding gender-based violence (GBV) cases in Latin America. The goal is to increase report levels, build trust in the justice system, and improve access to justice for women and LGBTIQ+ people. AymurAI will generate and maintain anonymized datasets from legal rulings to understand GBV and support policy making, and also contribute to feminist collectives' campaigns. @@ -83,7 +83,7 @@ You can also run the model through an AymurAI pipeline. ```python from aymurai.pipeline import AymurAIPipeline -pipeline = AymurAIPipeline.load("/resources/pipelines/production/full-paragraph") +pipeline = AymurAIPipeline.load("/resources/pipelines/production/datapublic") item = { 'path': 'dummy', diff --git a/docs/pipelines/README.md b/docs/pipelines/README.md index b09731d6..e62edfe0 100644 --- a/docs/pipelines/README.md +++ b/docs/pipelines/README.md @@ -14,11 +14,11 @@ This section documents AymurAI backend production pipelines by workflow. ## Production pipeline sources - Anonymizer config: `resources/pipelines/production/flair-anonymizer/pipeline.json` -- Datapublic config: `resources/pipelines/production/full-paragraph/pipeline.json` +- Datapublic config: `resources/pipelines/production/datapublic/pipeline.json` ## API mapping - `POST /anonymizer/predict` -> `flair-anonymizer` -- `POST /datapublic/predict/{document_id}` -> `full-paragraph` +- `POST /datapublic/predict/{document_id}` -> `datapublic` ## Related docs - API reference: [../api/README.md](../api/README.md) diff --git a/docs/pipelines/datapublic/README.md b/docs/pipelines/datapublic/README.md index 1d039516..53b4199c 100644 --- a/docs/pipelines/datapublic/README.md +++ b/docs/pipelines/datapublic/README.md @@ -24,7 +24,7 @@ This flow extracts structured information from paragraphs and supports document- ## Technical components ### Pipeline configuration -- Source: `resources/pipelines/production/full-paragraph/pipeline.json` +- Source: `resources/pipelines/production/datapublic/pipeline.json` - Preprocess: - `aymurai.models.flair.utils.FlairTextNormalize` - Models: @@ -58,7 +58,7 @@ Tables touched by this flow: - `datapublic_document_paragraph` ## Notes -- Current production pipeline directory name is `full-paragraph`. +- Current production pipeline directory name is `datapublic`. - `document_id` is the document-level grouping key used to associate paragraph predictions and validation payloads. - Validation persistence is document-level and intentionally accepts a free-form JSON object. - `GET /datapublic/validation/document/{document_id}` returns `404` when the document does not exist; `POST` upserts the validation payload. diff --git a/resources/pipelines/production/full-paragraph/pipeline.json b/resources/pipelines/production/datapublic/pipeline.json similarity index 100% rename from resources/pipelines/production/full-paragraph/pipeline.json rename to resources/pipelines/production/datapublic/pipeline.json From c979d2dd1bf819c3a9552d286ca9402d9b5b8e0e Mon Sep 17 00:00:00 2001 From: jed Date: Fri, 13 Mar 2026 12:37:19 -0300 Subject: [PATCH 091/101] ci(tests): add API + pipeline integration tests on linux and windows (#74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Merge dev into main for v1.1.12 (#57) * Update README.md * 🐛 bugfix: Fix XML special character escaping in DocAnonymizer * ➕ build(deps): Add python-docx package * ✨ feat: Add watermark and hyperlink functionality to document anonymization * ✨ feat: Install Archivo font in Dockerfile * 🎨 refactor: Improve Dockerfile structure and comments for clarity * ⏪ revert: Remove Archivo font installation from Dockerfile * 🔖 feat: Update aymurai package version to 1.1.11 in uv.lock * 🐛 Improve get_extension logic to fix document extraction issues on Windows and remove python-magic dependency * 🔧 Update Dockerfile to use 'bullseye' variant for Python images for improved compatibility * 🔧 Update Makefile targets for improved Docker workflow * 🔖 feat: Bump aymurai package version to 1.1.12 * ♻️ Harden get_extension with header scans and zip safeguards * 🔧 Extend document extraction timeout to 30s * 🔧 Refactor Docker workflow to build and push images using docker/build-push-action * 🔧 Fix workflow step order to correctly extract tag name before building Docker images * 🔧 Remove tag extraction step and use github.ref_name directly for Docker image builds * ⏪ Revert Docker workflow to extract tag name and use it for image versioning * Update .github/workflows/build-docker-image.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * ✏️ Remove incomplete comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: jed Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * WIP: feat(decision): ✨ integrate TinyEmbeddingBagClassifier for decision detection (#67) * feat(decision): ✨ integrate TinyEmbeddingBagClassifier for decision detection - Introduced a new model class `DecisionEmbeddingBagBinRegex` using `TinyEmbeddingBagClassifier`. - Updated model loading and saving mechanisms to support safetensors format. - Added a new training notebook for the embedding bag classifier. - Modified the pipeline configuration to include the new model. * ⚡️ Remove unidecode usage to avoid double normalization in model_input_from_text * 📝 Add type hints and docstrings for clarity in DecisionEmbeddingBagBinRegex and TinyEmbeddingBagClassifier * 🔧 Refactor import statements for safetensors to remove try-except block * 🔥 Remove Conv1dTextClassifier, Tokenizer and SpanishTokenizer implementations * 🐛 Fix gen_aymurai_entity call by removing unused category parameter * 🔖 Update aymurai package version to 2.0.0a4.dev1 * 🔀 cherry-pick(decision): modernize decision model and upgrade ML dependencies Cherry-pick TinyEmbeddingBagClassifier (safetensors) replacing Conv1d model. Remove dead deps (torchtext, pytorch-lightning), upgrade torch to 2.x and flair to 0.15.1. * 🐛 cherry-pick(fix): datapublic and anonymizer crash when use_cache is disabled * test(infra): rewrite test infrastructure with architecture guide standards - Delete old test files (test_document_extract.py, test_anonymizer_predict.py, test_datapublic_predict.py) - Create new directory structure: tests/integration/pipelines/, tests/api/routers/{anonymizer,datapublic,misc}/ - Rewrite tests/conftest.py: - Set env vars at module level (RESOURCES_BASEPATH=resources, SQLALCHEMY_DATABASE_URI=sqlite:///:memory:) - Remove torch mock and lazy loader - Direct imports from production code - Clean fixtures: db_engine (session-scoped), db_session (function-scoped), client (with dependency override) - Test data builders: build_data_item(), build_label(), build_anonymization_paragraph(), build_datapublic_paragraph() - Update pyproject.toml with [tool.pytest.ini_options]: strict-markers, integration/slow markers Verification: uv run python -c 'import tests.conftest' succeeds, pytest collection clean * test(conftest): add pipeline loading helpers and mock factories for API tests Wave 2 complete: integration pipeline conftest + API router conftest Integration pipeline conftest: - PIPELINE_CONFIGS dict for flair-anonymizer and full-paragraph - load_test_pipeline() helper with print_config=False - Session-scoped fixtures for both pipelines (expensive model loading) - build_pipeline_input() test data builder - sample_text fixture with Spanish legal text API router conftest: - build_mock_pipeline() factory with MagicMock - Mock preprocess/predict_single/postprocess methods - build_processed_data_item() test data builder - Re-exports builders from root conftest * test(api): add document extract endpoint tests with mocked extraction * test(api): add anonymizer and datapublic endpoint tests with mocked pipelines * test(integration): add pipeline integration tests for flair-anonymizer and full-paragraph * ✅ test: refactor test infrastructure and add integration tests - Reorganize test conftest files to proper hierarchy (tests/api/conftest.py) - Add pytest to dependency groups in pyproject.toml - Refactor API router tests to use centralized fixtures and builders - Add real document extraction tests with DOCX/PDF generators - Improve pipeline integration tests with fixture-based stages - Fix label serialization to use model_dump(mode="json") - Update UUID generation for datapublic tests to use uuid.uuid5 - Add cache path environment setup for integration tests - Clean up imports and remove unused dependencies - Remove empty test file (document_extract.py) This refactoring improves test maintainability, adds proper integration testing without excessive mocking, and establishes consistent test utilities across the codebase. * 👷 ci(github): add pytest workflow for CI integration - Introduced a new GitHub Actions workflow for running pytest. - Configured to trigger on pull requests and manual dispatch. - Supports multiple OS and Python versions for comprehensive testing. * 👷fix(tests): fix env variable DISKCACHE_ROOT * 👷 ci(github): remove deprecated PR tests workflow & fix env variable - Deleted the old PR tests workflow file. - This cleanup helps streamline CI processes and reduces redundancy. * ci(github): 👷 add pipeline download and integration tests to CI workflow - Introduced a new script for downloading pipelines. - Updated the pytest workflow to include running API and pipeline tests. - Enhanced test execution with improved output formatting and failure limits. * fix(tests): 🐛 avoid context manager in TestClient to skip app startup - Changed TestClient usage to prevent app lifespan startup during tests. - Ensured proper cleanup by closing the client after use. - This improves test performance and reliability. * 👷 ci(github): add RESOURCES_BASEPATH environment variable for pipeline tests - Added RESOURCES_BASEPATH to the environment variables for both downloading pipelines data and running pipeline tests. - This change ensures that the necessary resource paths are correctly set during the CI workflow execution. * 👷 ci(github): update RESOURCES_BASEPATH for pipeline data download - Changed RESOURCES_BASEPATH from /tmp to resources in the pipeline download step. - Ensures the correct path is used for resource access during tests. * chore(pyproject): 🔧 add environment markers for platform compatibility - Introduced required-environments for tool.uv to specify platform requirements. - Updated resolution-markers and required-markers in uv.lock for better dependency management. - Added tensorflow-io-gcs-filesystem with specific markers for Windows and Linux. * ci(github): 👷 configure es_AR locale for Ubuntu runners - Added steps to configure the es_AR locale on Ubuntu. - Ensures proper locale settings for tests running in the CI environment. * 👷 ci(github): add AYMURAI_CACHE_BASEPATH environment variable for pipeline tests - Introduced AYMURAI_CACHE_BASEPATH to the environment variables for both pipeline download and pipeline tests. - This change ensures that the correct cache path is utilized during the execution of the tests. * 🐛 fix(dependencies): adjust textract dependency for platform compatibility - Added conditional dependency for textract based on the operating system. - Specified different sources for textract depending on whether the platform is Windows or not. * 🔥 chore(opencode): remove opencode.json configuration file - Deleted the opencode.json file as it is no longer needed. - This change helps to clean up the repository and remove obsolete configurations. * 🚚 Update pipeline path for datapublic in scripts, notebooks and tests * 📝 docs: replace Black references with Ruff in CONTRIBUTING and Alembic hook examples * 🔧 Add backslash to default CACHE_BASEPATH value * 🔧 Update cache path retrieval to use settings for consistency * ➖ Remove textract dependencies and update documentation for extract_document function * ✅ Update integration tests and add new test cases for anonymizer and datapublic flows * 🔥 chore(test): remove legacy /test dir and standardize sample doc path to /resources/data/sample/document-01.docx * 🔧 Update UV_VERSION to latest in devcontainer Dockerfile * 🔧 Update dependency installation command to include all groups * 📌 Update uv.lock * 🐛 Fix CACHE_BASEPATH env alias resolution for CI pipeline downloads --- .devcontainer/Dockerfile | 2 +- .devcontainer/docker-compose.yml | 2 +- .devcontainer/entrypoint.sh | 2 +- .github/workflows/pytest.yml | 86 + .gitignore | 3 + .pre-commit-config.yaml | 15 +- .vscode/settings.json | 5 + aymurai/alembic.ini | 10 +- .../routers/anonymizer/anonymizer.py | 3 +- aymurai/models/decision/binregex.py | 3 +- aymurai/models/flair/core.py | 15 +- aymurai/scripts/pipelines_download.py | 19 + aymurai/settings.py | 8 +- aymurai/text/extraction.py | 2 +- .../sentence_transformer.py | 6 +- docs/CONTRIBUTING.md | 5 +- docs/api/README.md | 4 +- docs/es/api/README.md | 4 +- .../02-tensorflow-deprecation.ipynb | 251 +- ...ence-transformer-subcategorizer-eval.ipynb | 10 +- pyproject.toml | 21 +- test/api/mock-response/input/documento.docx | Bin 27682 -> 0 bytes ...oject-9-at-2022-11-10-22-41-09a5c372.conll | 174 -- ...roject-9-at-2022-11-10-22-41-09a5c372.json | 233 -- .../mock-response/output/mock-response.json | 94 - test/api/test_file.docx | Bin 4302 -> 0 bytes tests/__init__.py | 0 tests/api/__init__.py | 0 tests/api/conftest.py | 140 + tests/api/routers/__init__.py | 0 tests/api/routers/anonymizer/__init__.py | 0 .../api/routers/anonymizer/test_anonymizer.py | 353 +++ tests/api/routers/conftest.py | 27 + tests/api/routers/datapublic/__init__.py | 0 .../api/routers/datapublic/test_datapublic.py | 233 ++ tests/api/routers/misc/__init__.py | 0 .../api/routers/misc/test_document_extract.py | 303 ++ tests/api/routers/test_pipeline_flows.py | 172 ++ tests/integration/__init__.py | 0 tests/integration/pipelines/__init__.py | 0 tests/integration/pipelines/conftest.py | 63 + .../integration/pipelines/test_anonymizer.py | 95 + .../integration/pipelines/test_datapublic.py | 95 + uv.lock | 2442 +++++++++-------- 44 files changed, 2966 insertions(+), 1934 deletions(-) create mode 100644 .github/workflows/pytest.yml create mode 100644 aymurai/scripts/pipelines_download.py delete mode 100644 test/api/mock-response/input/documento.docx delete mode 100644 test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.conll delete mode 100644 test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.json delete mode 100644 test/api/mock-response/output/mock-response.json delete mode 100644 test/api/test_file.docx create mode 100644 tests/__init__.py create mode 100644 tests/api/__init__.py create mode 100644 tests/api/conftest.py create mode 100644 tests/api/routers/__init__.py create mode 100644 tests/api/routers/anonymizer/__init__.py create mode 100644 tests/api/routers/anonymizer/test_anonymizer.py create mode 100644 tests/api/routers/conftest.py create mode 100644 tests/api/routers/datapublic/__init__.py create mode 100644 tests/api/routers/datapublic/test_datapublic.py create mode 100644 tests/api/routers/misc/__init__.py create mode 100644 tests/api/routers/misc/test_document_extract.py create mode 100644 tests/api/routers/test_pipeline_flows.py create mode 100644 tests/integration/__init__.py create mode 100644 tests/integration/pipelines/__init__.py create mode 100644 tests/integration/pipelines/conftest.py create mode 100644 tests/integration/pipelines/test_anonymizer.py create mode 100644 tests/integration/pipelines/test_datapublic.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2cad5fca..9c303644 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.3 -ARG UV_VERSION=0.5.10 +ARG UV_VERSION=latest ARG CORE_IMAGE=ubuntu:noble FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS astral-uv-source diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 9477b892..c434079c 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -5,7 +5,7 @@ x-template: &template - ..:/workspace:cached - ../notebooks/:/notebooks - ../resources/:/resources - - ../test/:/test + - ../tests/:/tests - $HOME/.ssh/:/home/ubuntu/.ssh - /var/run/docker.sock:/var/run/docker.sock env_file: diff --git a/.devcontainer/entrypoint.sh b/.devcontainer/entrypoint.sh index 000c3360..5eddef91 100644 --- a/.devcontainer/entrypoint.sh +++ b/.devcontainer/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh # install dependencies -uv sync --frozen --all-extras +uv sync --frozen --all-extras --all-groups # configure precommit uv run pre-commit install diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 00000000..4636bc5c --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,86 @@ +name: pytest + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: pr-tests-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + pytest: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} + name: pytest (${{ matrix.os }}, py${{ matrix.python-version }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + python-version: + - "3.10" + # - "3.11" + # - "3.12" + # - "3.13" + # - "3.14" + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + version: latest + enable-cache: true + cache-dependency-glob: uv.lock + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Configure es_AR locale (Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install --yes locales + sudo locale-gen es_AR.UTF-8 + sudo update-locale LANG=es_AR.UTF-8 LC_ALL=es_AR.UTF-8 + echo "LANG=es_AR.UTF-8" >> "$GITHUB_ENV" + echo "LC_ALL=es_AR.UTF-8" >> "$GITHUB_ENV" + locale -a + + - name: Install dependencies + run: | + uv sync --frozen --python python --no-dev --no-managed-python --group tests + + - name: Run api tests + env: + DISKCACHE_ROOT: /tmp + run: uv run --no-sync pytest -q --tb=short --disable-warnings --color=yes --maxfail=5 tests/api + + - name: Download pipelines data + env: + DISKCACHE_ROOT: /tmp + RESOURCES_BASEPATH: resources + AYMURAI_CACHE_BASEPATH: resources/cache/aymurai + run: uv run --no-sync pipeline-download + + - name: Run pipeline tests + env: + DISKCACHE_ROOT: /tmp + RESOURCES_BASEPATH: resources + AYMURAI_CACHE_BASEPATH: resources/cache/aymurai + run: uv run --no-sync pytest -q --tb=short --disable-warnings --color=yes --maxfail=5 tests/integration/pipelines diff --git a/.gitignore b/.gitignore index effd04a3..2dad2bac 100755 --- a/.gitignore +++ b/.gitignore @@ -129,6 +129,9 @@ resources aymurai/version.py +.agents +.opencode.json +.sisyphus notebooks/** !notebooks/**/ !notebooks/**/*.ipynb diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2aeb0810..8f7ef35f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,11 @@ repos: - - repo: https://github.com/ambv/black - rev: 22.6.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.2 hooks: - - id: black - language_version: python3.10 + - id: ruff + - id: ruff-format - repo: https://github.com/kynan/nbstripout rev: 0.6.0 hooks: - - id: nbstripout - - # - repo: https://github.com/pre-commit/pre-commit-hooks - # rev: v2.20.0 - # hooks: - # - id: flake8 \ No newline at end of file + - id: nbstripout \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index dc48662f..68007232 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,9 @@ { + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, "python-envs.defaultEnvManager": "ms-python.python:venv", "python-envs.pythonProjects": [], "files.associations": { diff --git a/aymurai/alembic.ini b/aymurai/alembic.ini index b9a6bd2c..87f2df84 100644 --- a/aymurai/alembic.ini +++ b/aymurai/alembic.ini @@ -68,11 +68,11 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # on newly generated revision scripts. See the documentation for further # detail and examples -# format using "black" - use the console_scripts runner, against the "black" entrypoint -# hooks = black -# black.type = console_scripts -# black.entrypoint = black -# black.options = -l 79 REVISION_SCRIPT_FILENAME +# format using "ruff format" - use the exec runner, execute a binary +# hooks = ruff_format +# ruff_format.type = exec +# ruff_format.executable = ruff +# ruff_format.options = format REVISION_SCRIPT_FILENAME # lint with attempts to fix using "ruff" - use the exec runner, execute a binary # hooks = ruff diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 5d9b3cdc..65a36131 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -345,8 +345,7 @@ async def anonymizer_disambiguate( "List of per-paragraph predictions returned by /anonymizer/predict." ), ), - label_policies: dict[str, LabelPolicy] - | None = Body( + label_policies: dict[str, LabelPolicy] | None = Body( None, description=( "Optional per-label policy overrides for disambiguation/anonymization." diff --git a/aymurai/models/decision/binregex.py b/aymurai/models/decision/binregex.py index 9c6b0a61..bc67e2be 100644 --- a/aymurai/models/decision/binregex.py +++ b/aymurai/models/decision/binregex.py @@ -15,6 +15,7 @@ encode_text, make_offsets, ) +from aymurai.settings import settings from aymurai.utils.download import download from aymurai.utils.misc import get_element, is_url @@ -34,7 +35,7 @@ def __init__( self.threshold = threshold self.return_only_with_detalle = return_only_with_detalle - basepath = os.getenv("AYMURAI_CACHE_BASEPATH", "/resources/cache/aymurai") + basepath = settings.CACHE_BASEPATH if is_url(url := self._model_path): # Determine file extension from URL or default to safetensors if url.endswith(".pt"): diff --git a/aymurai/models/flair/core.py b/aymurai/models/flair/core.py index a3a5ad87..6fae5daf 100644 --- a/aymurai/models/flair/core.py +++ b/aymurai/models/flair/core.py @@ -1,20 +1,21 @@ +import logging import os import re -import logging from copy import deepcopy import flair import numpy as np from flair.data import Sentence -from more_itertools import collapse from flair.models import SequenceTagger +from more_itertools import collapse from aymurai.logger import get_logger -from aymurai.utils.misc import is_url -from aymurai.utils.download import download -from aymurai.meta.types import DataItem, DataBlock -from aymurai.meta.pipeline_interfaces import TrainModule from aymurai.meta.entities import Entity, EntityAttributes +from aymurai.meta.pipeline_interfaces import TrainModule +from aymurai.meta.types import DataBlock, DataItem +from aymurai.settings import settings +from aymurai.utils.download import download +from aymurai.utils.misc import is_url flair.logger.setLevel(logging.ERROR) @@ -46,7 +47,7 @@ def __init__( # load model if is_url(url := basepath): - basepath = os.getenv("AYMURAI_CACHE_BASEPATH", "/resources/cache/aymurai") + basepath = settings.CACHE_BASEPATH model_path = f"{basepath}/{self.__name__}/model.pt" logger.info(f"downloading model on {model_path}") os.makedirs(os.path.dirname(model_path), exist_ok=True) diff --git a/aymurai/scripts/pipelines_download.py b/aymurai/scripts/pipelines_download.py new file mode 100644 index 00000000..f797b613 --- /dev/null +++ b/aymurai/scripts/pipelines_download.py @@ -0,0 +1,19 @@ +import os + +from aymurai.logger import get_logger +from aymurai.pipeline.pipeline import AymurAIPipeline +from aymurai.settings import settings + +logger = get_logger(__name__) + +RESOURCES_BASEPATH = settings.RESOURCES_BASEPATH + + +def pipelines_download(): + logger.info("Loading pipelines and exit.") + AymurAIPipeline.load( + os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "flair-anonymizer") + ) + AymurAIPipeline.load( + os.path.join(RESOURCES_BASEPATH, "pipelines", "production", "datapublic") + ) diff --git a/aymurai/settings.py b/aymurai/settings.py index 51036fe8..3844d767 100644 --- a/aymurai/settings.py +++ b/aymurai/settings.py @@ -3,7 +3,7 @@ from pathlib import Path from dotenv import load_dotenv -from pydantic import ConfigDict, FilePath, field_validator +from pydantic import AliasChoices, ConfigDict, Field, FilePath, field_validator from pydantic_settings import BaseSettings import aymurai @@ -23,7 +23,7 @@ def load_env(): class Settings(BaseSettings): - model_config = ConfigDict(case_sensitive=True) + model_config = ConfigDict(case_sensitive=False) CORS_ORIGINS: list[str] | str = ",".join( [ @@ -50,6 +50,10 @@ def assemble_cors_origins(cls, v) -> list[str]: SQLALCHEMY_DATABASE_URI: str = "sqlite:////resources/cache/sqlite/database.db" RESOURCES_BASEPATH: str = "/resources" + CACHE_BASEPATH: str = Field( + default="/resources/cache", + validation_alias=AliasChoices("AYMURAI_CACHE_BASEPATH", "CACHE_BASEPATH"), + ) # Alembic Config for running migrations ALEMBIC_INI_PATH: FilePath = PARENT / "alembic.ini" diff --git a/aymurai/text/extraction.py b/aymurai/text/extraction.py index b57cb531..990eab6e 100644 --- a/aymurai/text/extraction.py +++ b/aymurai/text/extraction.py @@ -97,7 +97,7 @@ def extract_document( - If :const:`'ignore'`, then invalid parsing will be set as :const:`NaN` but not warn. use_cache (bool, optional): Toggle extractor-level caching. Defaults to True. - **kwargs: keyword arguments for textract. + **kwargs: keyword arguments for text extractors. Raises: ValueError: Invalid argument. diff --git a/aymurai/transforms/entity_subcategories/sentence_transformer.py b/aymurai/transforms/entity_subcategories/sentence_transformer.py index f662d043..790127f2 100644 --- a/aymurai/transforms/entity_subcategories/sentence_transformer.py +++ b/aymurai/transforms/entity_subcategories/sentence_transformer.py @@ -1,4 +1,3 @@ -import os from copy import deepcopy from pathlib import Path from typing import Iterable @@ -10,6 +9,7 @@ from aymurai.meta.types import DataItem from aymurai.models.sentence_encoder.base import BaseSentenceEncoder from aymurai.models.sentence_encoder.factory import create_encoder +from aymurai.settings import settings from aymurai.transforms.entity_subcategories.bm25 import BM25Scorer from aymurai.transforms.entity_subcategories.subcategories import SUBCATEGORIES from aymurai.transforms.entity_subcategories.utils import filter_by_category @@ -55,9 +55,7 @@ def __init__( self.bm25_weight = float(bm25_weight) self.batch_size = batch_size - cache_root = Path( - os.getenv("AYMURAI_CACHE_BASEPATH", "/resources/cache/aymurai") - ) + cache_root = Path(settings.CACHE_BASEPATH) self.cache_path = cache_root / self.__class__.__name__ self.cache_path.mkdir(parents=True, exist_ok=True) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 4c411155..c9b462da 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -47,7 +47,8 @@ pre-commit install ``` Configured hooks currently include: -- `black` +- `ruff` +- `ruff-format` - `nbstripout` This helps keep code formatting consistent and prevents notebook output from leaking into commits. @@ -56,7 +57,7 @@ This helps keep code formatting consistent and prevents notebook output from lea If needed, you can run the formatter manually before committing: ```bash -black aymurai/ +ruff format aymurai/ ``` ## Documentation policy diff --git a/docs/api/README.md b/docs/api/README.md index dd09f428..212e215e 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -173,7 +173,7 @@ curl -s http://localhost:8899/server/stats/summary ```bash curl -s -X POST \ - -F "file=@test/api/test_file.docx" \ + -F "file=@/resources/data/sample/document-01.docx" \ http://localhost:8899/misc/document-extract ``` @@ -241,7 +241,7 @@ curl -s -X POST http://localhost:8899/anonymizer/validation \ ```bash curl -X POST http://localhost:8899/anonymizer/anonymize-document \ - -F "file=@test/api/test_file.docx" \ + -F "file=@/resources/data/sample/document-01.docx" \ -F 'annotations={"data":[{"document":"Acusado: Ramiro Marrón DNI 34.555.666.","labels":[]}],"label_policies":{"PER":{"anonymize":true,"disambiguation":"fuzzy"}},"render_policy":{"suffix_mode":"auto","suffix_threshold":1}}' ``` diff --git a/docs/es/api/README.md b/docs/es/api/README.md index 15f4d1e9..adb3db4e 100644 --- a/docs/es/api/README.md +++ b/docs/es/api/README.md @@ -173,7 +173,7 @@ curl -s http://localhost:8899/server/stats/summary ```bash curl -s -X POST \ - -F "file=@test/api/test_file.docx" \ + -F "file=@/resources/data/sample/document-01.docx" \ http://localhost:8899/misc/document-extract ``` @@ -241,7 +241,7 @@ curl -s -X POST http://localhost:8899/anonymizer/validation \ ```bash curl -X POST http://localhost:8899/anonymizer/anonymize-document \ - -F "file=@test/api/test_file.docx" \ + -F "file=@/resources/data/sample/document-01.docx" \ -F 'annotations={"data":[{"document":"Acusado: Ramiro Marrón DNI 34.555.666.","labels":[]}],"label_policies":{"PER":{"anonymize":true,"disambiguation":"fuzzy"}},"render_policy":{"suffix_mode":"auto","suffix_threshold":1}}' ``` diff --git a/notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb b/notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb index d43dae54..b0ce826c 100755 --- a/notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb +++ b/notebooks/experiments/sentence-encoders/02-tensorflow-deprecation.ipynb @@ -103,23 +103,17 @@ " # Update span info if present in the current result.\n", " slot[\"text\"] = slot[\"text\"] or value.get(\"text\")\n", " slot[\"start\"] = (\n", - " slot[\"start\"]\n", - " if slot[\"start\"] is not None\n", - " else value.get(\"start\")\n", + " slot[\"start\"] if slot[\"start\"] is not None else value.get(\"start\")\n", " )\n", " slot[\"end\"] = (\n", - " slot[\"end\"]\n", - " if slot[\"end\"] is not None\n", - " else value.get(\"end\")\n", + " slot[\"end\"] if slot[\"end\"] is not None else value.get(\"end\")\n", " )\n", " slot[\"labels\"].extend(value.get(\"labels\", []))\n", " slot[\"choices\"].extend(value.get(\"choices\", []))\n", "\n", " for payload in merged.values():\n", " labels = [\n", - " label\n", - " for label in payload[\"labels\"]\n", - " if label in target_labels\n", + " label for label in payload[\"labels\"] if label in target_labels\n", " ]\n", " if not labels or payload[\"text\"] is None:\n", " continue\n", @@ -270,19 +264,14 @@ "source": [ "label_counts = samples_df.groupby(\"label\").size().sort_values(ascending=False)\n", "choice_counts = (\n", - " samples_df.explode(\"choices\")\n", - " .groupby([\"label\", \"choices\"])\n", - " .size()\n", - " .rename(\"count\")\n", + " samples_df.explode(\"choices\").groupby([\"label\", \"choices\"]).size().rename(\"count\")\n", ")\n", "\n", "summary_table = pd.DataFrame(\n", " {\n", " \"samples\": label_counts,\n", " \"unique_choices\": choice_counts.groupby(\"label\").size(),\n", - " \"avg_text_len\": samples_df.groupby(\"label\")[\"char_len\"]\n", - " .mean()\n", - " .round(1),\n", + " \"avg_text_len\": samples_df.groupby(\"label\")[\"char_len\"].mean().round(1),\n", " }\n", ")\n", "summary_table" @@ -297,9 +286,7 @@ "source": [ "fig, ax = plt.subplots()\n", "order = label_counts.index\n", - "sns.barplot(\n", - " x=label_counts.values, y=label_counts.index, ax=ax, palette=\"viridis\"\n", - ")\n", + "sns.barplot(x=label_counts.values, y=label_counts.index, ax=ax, palette=\"viridis\")\n", "ax.set_title(\"Samples per label\")\n", "ax.set_xlabel(\"Number of samples\")\n", "ax.set_ylabel(\"Label\")\n", @@ -314,9 +301,7 @@ ")\n", "\n", "for label in order:\n", - " subset = plot_data.query(\"label == @label\").sort_values(\n", - " \"count\", ascending=True\n", - " )\n", + " subset = plot_data.query(\"label == @label\").sort_values(\"count\", ascending=True)\n", " fig, ax = plt.subplots(figsize=(8, 4))\n", " sns.barplot(data=subset, x=\"count\", y=\"choices\", palette=\"viridis\", ax=ax)\n", " ax.set_title(f\"Top subcategories for {label}\")\n", @@ -379,9 +364,7 @@ "metadata": {}, "outputs": [], "source": [ - "pipeline_path = Path(\n", - " \"/resources/pipelines/production/full-paragraph/pipeline.json\"\n", - ")\n", + "pipeline_path = Path(\"/resources/pipelines/production/datapublic/pipeline.json\")\n", "\n", "with pipeline_path.open() as f:\n", " pipeline_config = json.load(f)\n", @@ -515,8 +498,7 @@ "def recompute_response_vectors(model):\n", " \"\"\"Return normalized subcategory strings and freshly encoded vectors.\"\"\"\n", " normalized = [\n", - " normalize_subcategory(subcategory)\n", - " for subcategory in model.subcategories\n", + " normalize_subcategory(subcategory) for subcategory in model.subcategories\n", " ]\n", " fresh_vectors = model.usem.encode(\n", " normalized,\n", @@ -556,19 +538,13 @@ "outputs": [], "source": [ "row_max_indices = np.argmax(cosine_matrix, axis=1)\n", - "row_max_values = cosine_matrix[\n", - " np.arange(len(row_max_indices)), row_max_indices\n", - "]\n", - "mismatched_rows = np.where(row_max_indices != np.arange(len(row_max_indices)))[\n", - " 0\n", - "]\n", + "row_max_values = cosine_matrix[np.arange(len(row_max_indices)), row_max_indices]\n", + "mismatched_rows = np.where(row_max_indices != np.arange(len(row_max_indices)))[0]\n", "diag_values = np.diag(cosine_matrix)\n", "off_identity_matches = (\n", " pd.DataFrame(\n", " {\n", - " \"subcategory\": [\n", - " model.subcategories[idx] for idx in mismatched_rows\n", - " ],\n", + " \"subcategory\": [model.subcategories[idx] for idx in mismatched_rows],\n", " \"matched_index\": row_max_indices[mismatched_rows],\n", " \"max_cosine\": row_max_values[mismatched_rows],\n", " \"diagonal_cosine\": diag_values[mismatched_rows],\n", @@ -695,9 +671,7 @@ "outputs": [], "source": [ "# Filter out samples with no annotated choices\n", - "retrieval_records = [\n", - " record for record in retrieval_records if record[\"choices\"]\n", - "]\n", + "retrieval_records = [record for record in retrieval_records if record[\"choices\"]]\n", "len(retrieval_records)" ] }, @@ -712,16 +686,11 @@ " metrics = []\n", " labels = sorted({record[\"label\"] for record in records})\n", " for label in labels:\n", - " label_records = [\n", - " record for record in records if record[\"label\"] == label\n", - " ]\n", + " label_records = [record for record in records if record[\"label\"] == label]\n", " total = len(label_records)\n", " for k in ks:\n", " hits = sum(\n", - " any(\n", - " choice in record[\"retrieved\"][:k]\n", - " for choice in record[\"choices\"]\n", - " )\n", + " any(choice in record[\"retrieved\"][:k] for choice in record[\"choices\"])\n", " for record in label_records\n", " )\n", " metrics.append(\n", @@ -735,15 +704,10 @@ " overall = []\n", " for k in ks:\n", " hits = sum(\n", - " any(\n", - " choice in record[\"retrieved\"][:k]\n", - " for choice in record[\"choices\"]\n", - " )\n", + " any(choice in record[\"retrieved\"][:k] for choice in record[\"choices\"])\n", " for record in records\n", " )\n", - " overall.append(\n", - " {\"label\": \"OVERALL\", \"k\": k, \"accuracy\": hits / len(records)}\n", - " )\n", + " overall.append({\"label\": \"OVERALL\", \"k\": k, \"accuracy\": hits / len(records)})\n", "\n", " metrics.extend(overall)\n", " return pd.DataFrame(metrics)\n", @@ -768,11 +732,7 @@ "\n", "sns.heatmap(\n", " baseline_accuracy_pivot.loc[\n", - " [\n", - " label\n", - " for label in baseline_accuracy_pivot.index\n", - " if label != \"OVERALL\"\n", - " ]\n", + " [label for label in baseline_accuracy_pivot.index if label != \"OVERALL\"]\n", " ],\n", " annot=True,\n", " fmt=\".2f\",\n", @@ -802,9 +762,7 @@ " record\n", " for record in retrieval_records\n", " if record[\"label\"] == label\n", - " and any(\n", - " choice in record[\"retrieved\"][:5] for choice in record[\"choices\"]\n", - " )\n", + " and any(choice in record[\"retrieved\"][:5] for choice in record[\"choices\"])\n", " ]\n", " for record in random.sample(label_records, k=min(3, len(label_records))):\n", " print(f\"- Text: {record['text']}\")\n", @@ -827,9 +785,7 @@ " record\n", " for record in retrieval_records\n", " if record[\"label\"] == label\n", - " and not any(\n", - " choice in record[\"retrieved\"][:5] for choice in record[\"choices\"]\n", - " )\n", + " and not any(choice in record[\"retrieved\"][:5] for choice in record[\"choices\"])\n", " ]\n", " for record in random.sample(label_records, k=min(3, len(label_records))):\n", " print(f\"- Text: {record['text']}\")\n", @@ -888,9 +844,7 @@ " self.subcategories = subcategories\n", " self._rng = random.Random(seed)\n", "\n", - " def batch_retrieve(\n", - " self, texts: list[str], top_k: int = 5\n", - " ) -> list[list[str]]:\n", + " def batch_retrieve(self, texts: list[str], top_k: int = 5) -> list[list[str]]:\n", " k = min(top_k, len(self.subcategories))\n", " if k == 0:\n", " return [[] for _ in texts]\n", @@ -937,9 +891,7 @@ " for variant, records in variant_records.items()\n", "}\n", "variant_pivots = {\n", - " variant: df.pivot(\n", - " index=\"label\", columns=\"k\", values=\"accuracy\"\n", - " ).sort_index()\n", + " variant: df.pivot(index=\"label\", columns=\"k\", values=\"accuracy\").sort_index()\n", " for variant, df in variant_accuracy.items()\n", "}\n", "\n", @@ -970,11 +922,7 @@ "\n", "sns.heatmap(\n", " variant_pivots[\"Random\"].loc[\n", - " [\n", - " label\n", - " for label in baseline_accuracy_pivot.index\n", - " if label != \"OVERALL\"\n", - " ]\n", + " [label for label in baseline_accuracy_pivot.index if label != \"OVERALL\"]\n", " ],\n", " annot=True,\n", " fmt=\".2f\",\n", @@ -1017,9 +965,7 @@ "\n", "\n", "class BM25Retriever:\n", - " def __init__(\n", - " self, subcategories: list[str], k1: float = 1.2, b: float = 0.75\n", - " ):\n", + " def __init__(self, subcategories: list[str], k1: float = 1.2, b: float = 0.75):\n", " self.subcategories = subcategories\n", " normalized = [normalize_subcategory(sub) for sub in subcategories]\n", " self.tokenized = [tokenize(text) for text in normalized]\n", @@ -1057,9 +1003,7 @@ " scores[idx] += idf * freq * (self.k1 + 1) / denom\n", " return scores\n", "\n", - " def batch_retrieve(\n", - " self, texts: list[str], top_k: int = 5\n", - " ) -> list[list[str]]:\n", + " def batch_retrieve(self, texts: list[str], top_k: int = 5) -> list[list[str]]:\n", " return [self._topk(text, top_k) for text in texts]\n", "\n", " def _topk(self, text: str, top_k: int) -> list[str]:\n", @@ -1080,9 +1024,7 @@ " self.response_vectors = usem_model.usem_vectors\n", " self.bm25_weight = bm25_weight\n", "\n", - " def batch_retrieve(\n", - " self, texts: list[str], top_k: int = 5\n", - " ) -> list[list[str]]:\n", + " def batch_retrieve(self, texts: list[str], top_k: int = 5) -> list[list[str]]:\n", " query_vectors = self.usem_model.usem.batch_encode(\n", " [normalize_text(text) for text in texts],\n", " encoder_type=\"question_encoder\",\n", @@ -1094,10 +1036,7 @@ " bm25_norm = bm25_scores / (bm25_scores.max() + 1e-9)\n", " sim_scores = similarity[row_idx]\n", " sim_norm = sim_scores / (sim_scores.max() + 1e-9)\n", - " combined = (\n", - " self.bm25_weight * bm25_norm\n", - " + (1 - self.bm25_weight) * sim_norm\n", - " )\n", + " combined = self.bm25_weight * bm25_norm + (1 - self.bm25_weight) * sim_norm\n", " k = min(top_k, combined.shape[0])\n", " top_indices = np.argsort(-combined)[:k]\n", " combined_results.append(\n", @@ -1114,13 +1053,10 @@ "outputs": [], "source": [ "bm25_models = {\n", - " label: BM25Retriever(subcats)\n", - " for label, subcats in label_subcategories.items()\n", + " label: BM25Retriever(subcats) for label, subcats in label_subcategories.items()\n", "}\n", "hybrid_models = {\n", - " label: HybridRetriever(\n", - " usem_models[label], bm25_models[label], bm25_weight=0.4\n", - " )\n", + " label: HybridRetriever(usem_models[label], bm25_models[label], bm25_weight=0.4)\n", " for label in usem_models\n", "}" ] @@ -1148,9 +1084,7 @@ " for variant, records in variant_records.items()\n", "}\n", "variant_pivots = {\n", - " variant: df.pivot(\n", - " index=\"label\", columns=\"k\", values=\"accuracy\"\n", - " ).sort_index()\n", + " variant: df.pivot(index=\"label\", columns=\"k\", values=\"accuracy\").sort_index()\n", " for variant, df in variant_accuracy.items()\n", "}\n", "\n", @@ -1162,11 +1096,7 @@ "\n", " sns.heatmap(\n", " pivot.loc[\n", - " [\n", - " label\n", - " for label in baseline_accuracy_pivot.index\n", - " if label != \"OVERALL\"\n", - " ]\n", + " [label for label in baseline_accuracy_pivot.index if label != \"OVERALL\"]\n", " ],\n", " annot=True,\n", " fmt=\".2f\",\n", @@ -1281,9 +1211,7 @@ " similarity = np.inner(query_vectors, self.response_vectors)\n", " k = min(top_k, similarity.shape[1])\n", " top_indices = np.argsort(-similarity, axis=1)[:, :k]\n", - " return [\n", - " [self.subcategories[idx] for idx in row] for row in top_indices\n", - " ]\n", + " return [[self.subcategories[idx] for idx in row] for row in top_indices]\n", "\n", "\n", "class HybridSentenceTransformerRetriever:\n", @@ -1312,21 +1240,14 @@ " bm25_norm = bm25_scores / (bm25_scores.max() + 1e-9)\n", " sim_scores = similarity[row_idx]\n", " sim_norm = sim_scores / (sim_scores.max() + 1e-9)\n", - " combined = (\n", - " self.bm25_weight * bm25_norm\n", - " + (1 - self.bm25_weight) * sim_norm\n", - " )\n", + " combined = self.bm25_weight * bm25_norm + (1 - self.bm25_weight) * sim_norm\n", " k = min(top_k, combined.shape[0])\n", " top_indices = np.argsort(-combined)[:k]\n", - " combined_results.append(\n", - " [self.subcategories[idx] for idx in top_indices]\n", - " )\n", + " combined_results.append([self.subcategories[idx] for idx in top_indices])\n", " return combined_results\n", "\n", "\n", - "def build_challenger_models(\n", - " usem_configs, encoder, encoder_name, batch_size=256\n", - "):\n", + "def build_challenger_models(usem_configs, encoder, encoder_name, batch_size=256):\n", " return {\n", " label: SentenceTransformerRetriever(\n", " label=label,\n", @@ -1339,9 +1260,7 @@ " }\n", "\n", "\n", - "def build_hybrid_sentence_transformer_models(\n", - " base_models, bm25_models, bm25_weight=0.4\n", - "):\n", + "def build_hybrid_sentence_transformer_models(base_models, bm25_models, bm25_weight=0.4):\n", " return {\n", " label: HybridSentenceTransformerRetriever(\n", " base_retriever=base_models[label],\n", @@ -1370,9 +1289,7 @@ " return records\n", "\n", "\n", - "distiluse_models = build_challenger_models(\n", - " usem_configs, distiluse_encoder, \"DistilUSE\"\n", - ")\n", + "distiluse_models = build_challenger_models(usem_configs, distiluse_encoder, \"DistilUSE\")\n", "minilm_models = build_challenger_models(usem_configs, minilm_encoder, \"MiniLM\")" ] }, @@ -1414,11 +1331,7 @@ "fig, ax = plt.subplots()\n", "sns.heatmap(\n", " distiluse_accuracy_pivot.loc[\n", - " [\n", - " label\n", - " for label in distiluse_accuracy_pivot.index\n", - " if label != \"OVERALL\"\n", - " ]\n", + " [label for label in distiluse_accuracy_pivot.index if label != \"OVERALL\"]\n", " ],\n", " annot=True,\n", " fmt=\".2f\",\n", @@ -1622,9 +1535,7 @@ " \"TensorFlow USEM\": baseline_accuracy_pivot.loc[\"OVERALL\"],\n", " \"DistilUSE\": distiluse_accuracy_pivot.loc[\"OVERALL\"],\n", " \"MiniLM\": minilm_accuracy_pivot.loc[\"OVERALL\"],\n", - " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"].loc[\n", - " \"OVERALL\"\n", - " ],\n", + " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"].loc[\"OVERALL\"],\n", " \"DistilUSE + BM25\": distiluse_hybrid_pivot.loc[\"OVERALL\"],\n", " \"MiniLM + BM25\": minilm_hybrid_pivot.loc[\"OVERALL\"],\n", " }\n", @@ -1707,7 +1618,7 @@ "outputs": [], "source": [ "# Per-label comparison for top-1 accuracy\n", - "label_index = [l for l in baseline_accuracy_pivot.index if l != \"OVERALL\"]\n", + "label_index = [label for label in baseline_accuracy_pivot.index if label != \"OVERALL\"]\n", "label_comparison = pd.DataFrame(\n", " {\n", " \"TensorFlow USEM\": baseline_accuracy_pivot.loc[label_index, 1],\n", @@ -1878,9 +1789,7 @@ " \"Top-5\": comparison_df[5].values,\n", " \"Δ vs Baseline (Top-1)\": [\n", " 0.0 if model == \"TensorFlow USEM\" else (score - baseline_score)\n", - " for model, score in zip(\n", - " comparison_df.index, comparison_df[1].values\n", - " )\n", + " for model, score in zip(comparison_df.index, comparison_df[1].values)\n", " ],\n", " }\n", ").set_index(\"Model\")\n", @@ -1944,7 +1853,7 @@ "outputs": [], "source": [ "# Best-performing model per label and k (excluding TensorFlow USEM + BM25)\n", - "label_index = [l for l in baseline_accuracy_pivot.index if l != \"OVERALL\"]\n", + "label_index = [label for label in baseline_accuracy_pivot.index if label != \"OVERALL\"]\n", "ks = [k for k in range(1, 6) if k in baseline_accuracy_pivot.columns]\n", "\n", "model_pivots = {\n", @@ -1971,10 +1880,7 @@ "scores = pd.DataFrame(records)\n", "best_models = scores.loc[scores.groupby([\"label\", \"k\"])[\"accuracy\"].idxmax()]\n", "model_ranking = (\n", - " best_models[\"model\"]\n", - " .value_counts()\n", - " .rename_axis(\"model\")\n", - " .reset_index(name=\"wins\")\n", + " best_models[\"model\"].value_counts().rename_axis(\"model\").reset_index(name=\"wins\")\n", ")\n", "\n", "best_models.reset_index(drop=True)\n", @@ -1988,9 +1894,7 @@ "metadata": {}, "outputs": [], "source": [ - "scores.query(\"k == 1\").sort_values(\n", - " [\"label\", \"accuracy\"], ascending=[True, False]\n", - ")" + "scores.query(\"k == 1\").sort_values([\"label\", \"accuracy\"], ascending=[True, False])" ] }, { @@ -2000,9 +1904,7 @@ "metadata": {}, "outputs": [], "source": [ - "scores.query(\"k == 3\").sort_values(\n", - " [\"label\", \"accuracy\"], ascending=[True, False]\n", - ")" + "scores.query(\"k == 3\").sort_values([\"label\", \"accuracy\"], ascending=[True, False])" ] }, { @@ -2012,9 +1914,7 @@ "metadata": {}, "outputs": [], "source": [ - "scores.query(\"k == 5\").sort_values(\n", - " [\"label\", \"accuracy\"], ascending=[True, False]\n", - ")" + "scores.query(\"k == 5\").sort_values([\"label\", \"accuracy\"], ascending=[True, False])" ] }, { @@ -2041,8 +1941,7 @@ "\n", "# Cache texts per label\n", "label_texts = {\n", - " label: [item[\"text\"] for item in items]\n", - " for label, items in samples_by_label.items()\n", + " label: [item[\"text\"] for item in items] for label, items in samples_by_label.items()\n", "}\n", "\n", "# Cache BM25 score vectors per label to avoid recomputing across weights\n", @@ -2087,9 +1986,7 @@ " combined = bm25_weight * bm25_norm + (1 - bm25_weight) * sim_norm\n", " k = min(top_k, combined.shape[0])\n", " top_indices = np.argsort(-combined)[:k]\n", - " combined_results.append(\n", - " [bm25_model.subcategories[i] for i in top_indices]\n", - " )\n", + " combined_results.append([bm25_model.subcategories[i] for i in top_indices])\n", " return combined_results\n", "\n", "\n", @@ -2112,9 +2009,7 @@ " bm25_weight=weight,\n", " top_k=5,\n", " )\n", - " for item, retrieved in zip(\n", - " samples_by_label[label], retrieved_lists\n", - " ):\n", + " for item, retrieved in zip(samples_by_label[label], retrieved_lists):\n", " records.append(\n", " {\n", " \"label\": label,\n", @@ -2142,9 +2037,9 @@ "search_df = pd.DataFrame(search_results)\n", "\n", "# Best weight per model by top-1 accuracy\n", - "best_rows = search_df.loc[\n", - " search_df.groupby(\"model\")[\"top1\"].idxmax()\n", - "].reset_index(drop=True)\n", + "best_rows = search_df.loc[search_df.groupby(\"model\")[\"top1\"].idxmax()].reset_index(\n", + " drop=True\n", + ")\n", "best_pivots = {\n", " row.model: pivot_lookup[(row.model, row.bm25_weight)]\n", " for _, row in best_rows.iterrows()\n", @@ -2186,9 +2081,7 @@ "for row in best_rows.itertuples():\n", " model_name = row.model\n", " weight = row.bm25_weight\n", - " base_models = (\n", - " distiluse_models if model_name == \"DistilUSE\" else minilm_models\n", - " )\n", + " base_models = distiluse_models if model_name == \"DistilUSE\" else minilm_models\n", "\n", " records = []\n", " for label, base_retriever in base_models.items():\n", @@ -2215,24 +2108,22 @@ " )\n", "\n", " accuracy = compute_topk_accuracy(records)\n", - " pivot = accuracy.pivot(\n", - " index=\"label\", columns=\"k\", values=\"accuracy\"\n", - " ).sort_index()\n", + " pivot = accuracy.pivot(index=\"label\", columns=\"k\", values=\"accuracy\").sort_index()\n", " tuned_records[model_name] = records\n", " tuned_pivots[model_name] = pivot\n", "\n", "# Show tuned weights and overall performance\n", "best_rows_display = best_rows.copy()\n", "for model_name, pivot in tuned_pivots.items():\n", - " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top1\"] = (\n", - " pivot.loc[\"OVERALL\", 1]\n", - " )\n", - " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top3\"] = (\n", - " pivot.loc[\"OVERALL\", 3]\n", - " )\n", - " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top5\"] = (\n", - " pivot.loc[\"OVERALL\", 5]\n", - " )\n", + " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top1\"] = pivot.loc[\n", + " \"OVERALL\", 1\n", + " ]\n", + " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top3\"] = pivot.loc[\n", + " \"OVERALL\", 3\n", + " ]\n", + " best_rows_display.loc[best_rows_display[\"model\"] == model_name, \"top5\"] = pivot.loc[\n", + " \"OVERALL\", 5\n", + " ]\n", "\n", "best_rows_display" ] @@ -2279,14 +2170,12 @@ "comparison_df_tuned = pd.DataFrame(\n", " {\n", " \"TensorFlow USEM\": baseline_accuracy_pivot.loc[\"OVERALL\"],\n", - " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"].loc[\n", - " \"OVERALL\"\n", - " ],\n", + " \"TensorFlow USEM + BM25\": variant_pivots[\"USEM + BM25 hybrid\"].loc[\"OVERALL\"],\n", " \"DistilUSE\": distiluse_accuracy_pivot.loc[\"OVERALL\"],\n", " \"MiniLM\": minilm_accuracy_pivot.loc[\"OVERALL\"],\n", - " \"DistilUSE + BM25 tuned\": tuned_hybrid_pivots[\n", - " \"DistilUSE + BM25 tuned\"\n", - " ].loc[\"OVERALL\"],\n", + " \"DistilUSE + BM25 tuned\": tuned_hybrid_pivots[\"DistilUSE + BM25 tuned\"].loc[\n", + " \"OVERALL\"\n", + " ],\n", " \"MiniLM + BM25 tuned\": tuned_hybrid_pivots[\"MiniLM + BM25 tuned\"].loc[\n", " \"OVERALL\"\n", " ],\n", @@ -2313,7 +2202,7 @@ " \"MiniLM + BM25 tuned\": tuned_hybrid_pivots[\"MiniLM + BM25 tuned\"],\n", "}\n", "\n", - "labels_no_overall = [l for l in label_index]\n", + "labels_no_overall = [label for label in label_index]\n", "\n", "for name, pivot in pivot_map.items():\n", " fig, ax = plt.subplots(figsize=(6, 4))\n", @@ -2344,7 +2233,7 @@ ], "metadata": { "kernelspec": { - "display_name": "aymurai (3.10.19)", + "display_name": "aymurai (3.10.12)", "language": "python", "name": "python3" }, @@ -2358,7 +2247,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.19" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb b/notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb index 83546815..1c271ecf 100644 --- a/notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb +++ b/notebooks/experiments/sentence-encoders/04-sentence-transformer-subcategorizer-eval.ipynb @@ -46,7 +46,7 @@ "outputs": [], "source": [ "# Config\n", - "pipeline_path = Path(\"/resources/pipelines/production/full-paragraph/pipeline.json\")\n", + "pipeline_path = Path(\"/resources/pipelines/production/datapublic/pipeline.json\")\n", "annotations_path = Path(\n", " \"/resources/annotations/label-studio/resos-annotations/30-nov/project-3-at-2022-11-30-16-04-2b43bf39.json\"\n", ")\n", @@ -295,7 +295,7 @@ "# Heatmap\n", "fig, ax = plt.subplots()\n", "sns.heatmap(\n", - " pivot.loc[[l for l in pivot.index if l != \"OVERALL\"]],\n", + " pivot.loc[[label for label in pivot.index if label != \"OVERALL\"]],\n", " annot=True,\n", " fmt=\".2f\",\n", " cmap=\"YlGnBu\",\n", @@ -372,7 +372,7 @@ "# Heatmap\n", "fig, ax = plt.subplots()\n", "sns.heatmap(\n", - " pivot.loc[[l for l in pivot.index if l != \"OVERALL\"]],\n", + " pivot.loc[[label for label in pivot.index if label != \"OVERALL\"]],\n", " annot=True,\n", " fmt=\".2f\",\n", " cmap=\"YlGnBu\",\n", @@ -398,7 +398,7 @@ ], "metadata": { "kernelspec": { - "display_name": "aymurai (3.10.19)", + "display_name": "aymurai (3.10.12)", "language": "python", "name": "python3" }, @@ -412,7 +412,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.19" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/pyproject.toml b/pyproject.toml index 4e6e0475..b5bb6d6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ authors = [ maintainers = [ { name = "jedzill4", email = "r@collectiveai.io" }, { name = "jansaldo", email = "juli@collectiveai.io" }, - ] description = "The backend API and machine learning models of AymurAI." readme = "README.md" @@ -71,8 +70,7 @@ dependencies = [ "cachetools>=5.5.0", "diskcache>=5.6.3", "scipy<1.14.1", - "torch>=1.13.1", - "pytorch-lightning==1.8.3.post1", + "torch>=2.0", "psutil==6.1.0", "sqlmodel==0.0.22", "alembic>=1.13.3", @@ -102,12 +100,14 @@ dev = [ "jupyter>=1.1.1", "pip>=24.3.1", ] +tests = ["pytest>=9.0.2"] [tool.setuptools.packages.find] include = ["aymurai"] [project.scripts] aymurai-api = "aymurai.api.main:main" +pipeline-download = "aymurai.scripts.pipelines_download:pipelines_download" [tool.setuptools.package-data] @@ -118,6 +118,21 @@ version_file = "aymurai/version.py" version_scheme = "only-version" local_scheme = "no-local-version" +[tool.uv] +required-environments = [ + "sys_platform == 'linux' and platform_machine == 'x86_64'", + "sys_platform == 'win32' and platform_machine == 'AMD64'", +] + [tool.pylint.messages_control] disable = "C0330, C0326" + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = ["-v", "--tb=short", "--strict-markers", "--disable-warnings"] +markers = [ + "integration: marks tests as integration tests", + "slow: marks tests as slow (model loading, >30s)", + "external: marks tests that depend on optional external binaries/services", +] diff --git a/test/api/mock-response/input/documento.docx b/test/api/mock-response/input/documento.docx deleted file mode 100644 index 51b2a7cd853e7fe168b1926a9995d149d64fabcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27682 zcmeFXW3O=C)~&g0+qP}nwr$(CZS!8XeJ|U#ZM&Z5tyIpg$~pTN)J$eFGd^Ce_tyI8 znF`XtASeJ35D);PMUJWf|M4LH^KD{ptl(tt;7o69?_^5nVQb^7Br6Y0h|u#%&9+5Z zuU1u}i_DtG!sr+~g578=ik=40vZ7Bvht`LM499Gb7oW*4BfsOS-xK&~#lSoyhN5b4 zG)KvtU%2@KgkLxTZpbwc4`yZoikHjct!&wWgQL?m5Cje7gBluhx-c{643UO>aLjD6 zFP>;pq z44pk-(eE@==Y3>}s+#7i#4bAY`$miu38I&G047#~tiLF9a3 zy4*TK`ufoUJi$p1!=tE6FJQ2}JyWTGWqHrtK9$|Cq@a6e6c%?qtjl$ZWbSf`EQ2(0 zGwbpF(T{&(6uuYnQy(F#8BCMpBw2SYC-R+B{+?`U{$|W$G^uRy$M7NaSp7>O z2_qcUQ+-(5e*W&350HX1IK*^2{bD#E001U10D!{(QALFRRMF7E;a?GR6ZPf)GN26a zexpqOrM~=$l#q%Pl97o>Vi;go>VmFX-zcL6>FseZn9RFYp5<-pTije&@2Chy)*x@m z3X?<$2c#&Ju7F#-EB3&Q8|?S2mC~~YUAzOL%W2^5Y}^QVM$!oYIW99i?#Vg+Y)E2wI0-uJ)p5h5U2hQEnH4_8HV4} z5E{1?ZrezUD`hm>YIV_boD3DmkWP9^Owubfw=1n%35!lkGd=Xvp8d-+37?h?CA`4a zlI&88J-8f7s|U^VO{h`jT)*D+L{!%!4(am6hN#B#t)XTnUbe=n9c+cWwm^r&4 zdy7i3c??@CI)>?m&v+HTO&Yd!pJuzw^BjV7h%6Ww86XSGf=3jx{{xh2e+xqwBDkW`>H#;wTpZ~b3gSgzaczb&7ESCA1O$%X!=tr z5Cm&Fg1b!^^3L3TEUYfOV7v}le3}P`89Q9 zK;Y)!9Hzh56-A5b@m78RV*%h02fh2aNB^KW{0G?o*%JP@uo(Y;SpVA;{QtnRFf}y! z&&rC8pN9I&fDm%aC%V?Py%HRe@j@h2r-V;HvdOaXl^`V-Of%E@vrGF*>S6fp%m;Y5BDU)*l(@Wn_ zlimj;0+xzNb_hys6cRSN<_`$>!Y?TEeWOI7WlE;5`*gQ!y{B=-`j#!JI6cj{ z%sM^~a2k1K(q^Z4&0LQpn`I?#nvNTm7)7DVPM>vBtu zxUnYu?g9>!MSy^405uf)i+)1n{tCYu=p_{E52P+vB9+44G*Rs|Zt(8#Xb4B@b^Ihj zIst4th1hj&X3oa_F zlg(EcXu?9%YJW4;#6;bqD@uxsBA7GuyFKBzpN*^+<(fEemlP-^s-sin{tRpLTB7N~ zof@-l_V%qV5&RMLm7P*nmg;J^|B4n#xh_)`%ca6C(_&-*3zQIHv=i#kiLiV}g>iF2 z&^s+{glCxY1QydX&L)@3Lw~N>U1SHAve-6i-AJrl{=o7M@#Q zecX`&h~nDbVPk15Uis4R!Kq1^Vd&;WE9k;jMy0e4KQKl-SGf-%F`S^3nUo+?<+Qev zPhPMJMqXgb+tiqxn>>cgbShqSWFH+B8%+>sX>X{e!fNFe_}JvAG{d!2>{7cc%4h|kD_4;IhAZClqNJ*DR#C&$L%& zI-RT1_8oqbh(*Xu$GQG})2$J^7`}eYrN?WC=D3QT0s9`PclUB**~$1$7g%q1n>#%$ zh?lVNo8B|d9rOV!A%qPf`UX2!7M3jzf!4Q}s|N0bJ9|FbG&aDh){b|%0O<9{u8-Jj z(^04md~&1(P#YKHC0+&W9s%Gx2~$)AzcmkPtLtdUk#Z_J5&=~S|1x*t;~+kispAg0 zbf{s6xquD+P2{$H3me#b!HBG*IIR=N1Y>A{iaA0%bXJU2=u`HI4lC>OlFIv)R6|~6 z8#6jgE#W}{Avy6VH|7FJV`MQ%NoEqNkO?k`VkGojxig3Gd&Pwu&D$a zs31oBS2&bO5T`JSOBkBbAhd%|GSwh3Agk;WOM~5S9dia0CQRi>BAE`=z6Vg`fDs?y zaSVJ0Uc1v5bj4vf&&fbZz*m5XQAI$i4^YJj747meNRc4kAh_m5Gb8U&KwA?FScgD= z(G$5wTH8x)E^cHy+Y8C<)8Acv(&S}iVpkBpxnnkJ5_T|JEeoZ0uhqg+>d_^PnP04n zoU;wamX({s_zGWcu@5cDsclaw&}M3-okcrPewjLO{>8dBa&6%rpy!lus}1q#79`Nm zhXi_vLaV1{{i|(MUfW->r}oDJRPyUhS>}G5c_uKdAUwg4XzKBtP`9)qFhUU)rK!`uDQzwaCH5wf6~b!>Vbh+xsvXh#U247(ncZ( zSRg5P_YD3CdE;Cg{}BHYs9T~@S-T;%!klV4V#xIMYQV@HaNT}a*PK#Ii4G~P4fZqD zlpqTkjr6_SR%T?ocohr@=@d~;Q*Ltv7ImcS@ph+H%W(*i;x|*sLjJ?!r_BtJ`)+35|BKIz_J`=!AjYm}d~F)5k823ResbbGr0gVN1_BkxC!w@-8;0By*jSsJMq{4^YnU#LI8Gg# ztS!=IVw|<89ejPx1C)j8))J+^sfOF57z5oUmdzh;gVa2_NNjS9u44v}y&Z+?H5{PTIdrc~B*pDDDC?Nofl93^9#Kf}%S?#?@RBs!8{|A_YpOAD)4| zv>tQ`blj@iRzr+g&-G45$xmMlzN&TPh;+T8OTB1@Ede37J_LMjC3()7=Y=;8%yO`(+~suykH z27wdomVA8ttejxbR&y+}#t3|Q77@g>S&Bp-iY#l?lKeFROU<@QGm#h=4k=>wtN`as z|F~e-DT{adSl-4RJ0>s zc`bzjXH=0UljqovbB&#VYyFVLKr*=iDr6$G93ZS#O;I6ejd}6$q+pS|kZV6`m#qPD z_X@RQJ&~&2ns0brIEQ&v#(~7rXML>Ejxn2TauM`+EzKXc1KKQEN`_-W6KbjB_AK-y zWl5uDm1jeU6}gSLxnvZ%ta2 zO~>j=%!<%o&s4AN6umiy+!s~npZy;87p|WaN5x;}L@yR|af1{CZJ9`?qh4xSRZJ>RBaASS!&*HZU&ng?(RiE&RUJzQm<2RIDj^nx| z=^;LeBSwsup6k98UJ00j1y8+E!U#6-;RP+0`Y=AuP(05df<7nk zg$$@={aNy0zG^Q)1NPz-@I8^+UJps`!VMg+s`l_;f~h?^6KIPepFVOUPeI%ILdS4E z3+s}>V~Xq$KC#Rqb!a`tVua0nil-F%YOG#SbD$>D@3bkqzqyNZS`-*C3Tn>0 zt$;Qzn$Ss}SJQg{aBo46$GA*R3Ay{0JC0?8UQN+@`5Uje8(viAntBSZA}80Qjhgb4 z?t3|G7hC;Qcq7M*Dwk+Sb&>(vaTL*3jIPkxS z`pWx7|8&cFw&|76`__DW5|C>5Lt~wjgOd|8qrWUxe?KdRm>mp`J z085MOqY8AI2Ed643d%_;%3t1>72Ay!zQCl^sEe>la{6Zoj^b#L5Is{(gxIIAH-6Cs ziJr**7F$$$ba;AjlH5!AYUP?6DoSdCs?rL+x!Kj_`3axT*UZYy&d~CB?`>gxeRz3# zmY;wD0u2WV7a1EpKOrL}@BYTuY$g0EM}C;J)btoNpBfu0J4;(@bA$T>94tIck+@jD zyv*F}^u(X0fKg*>_BnQ1UwboTH{3riPCmk4{&!dutbpM2_t-xw`WXtHlav&c^iE&L z>b47bdZh&U2@QozvfSGyqd-i$gJfpx8x{kQ2-+87xXp%*w1Q! z=r=y$XI=jfHXTbqV9?csCrUK{#NO9=IY};h>IW6dc^JTU0KmwrY|WMpMP;pa+<6+B zdkE1Y_#+?+zH@2N~0SF4Ku_4ftd z{?+&l6|!WSKwUUL=GuK3x!HOdT!=@8A*^0`>lyJiKB2Birnrz1{hBtWh5xkv+%_oz zV?yB(ct-NT-5SCe_Ao<*DZM?8o8o(G)R{D!zdE|Xwz~&h-{8!ff>-| z-5~98+bk3XZf$mG@EUM*S~U?}8T9GhKOVj*L?M^&;^wfAAD%EQ>-d;`P*3_SFr(G- zF!7LwNsq1SE&PLe)}=JjT*93+`9v#}9U^QtVMuyDx-q%AtWe{tl1@@qGo>I#q^2@+k=__rTm! zc+#lXm{gejY(n@rJ)Tm5HwVL)aDiCU?uw-%_btZ{9+t3wfU`$fXc}ltFFdG)GJAfW z%R=Uvj zXY3;rS~@Y1HTLA}p+VMcQa}ttrPb|4{*n12Wk&IlTT6_3yn)P!wtGT=qxXQ0Zaw(Gd*(XJ6^=#00` zyY!tl-v$@Qh*fq;3BYIsj&zwBipagT**>Lm@q7qq3-A%D_3*DqU(%|@NA>hU`vC~z z0RTn|n@L>%+DjCrreU3mD5cZ%njUM8B%>KM$XzREps3u(H zIQV+|5XoprX#?HowH4?tqNJ-sj%E;>)Jb6NxgX34WmVTEXWUkU{aVP0*4}^o>{aRB zXX;}A!qW#L(ve(SJG(dVmrEy!U~6~JY#BLHwT`h1tkKR^8mXl@o`qT3MsPf!#-8>* z*u;VF5UN+rq3jWj!6Vd!`}oVt;26!%vz4w_bN9DSS^1|qyi8=iX5mh=QYGCG?D8%o z9rE-GRKws3561v{Ck#w|%-HQs@!*u6k=Q;LNxg0o{V5n%MZF?;|h5my@SboMi}g z*Jc%#%8XTkj!iHCq1vHw6czc>OE<^>WH8*!seV2%^uQMWwCXHY-0^g(vqv)LqQlRR zAsu6V?Ezr`a)y5RlTby3)8HRL5@rp|vY2No^0R@c*SkKscQJavD=W@ zO3?Tu>o|!y0DoMaj^BxcnR`9|EHhw?_t%?d!txx=w6Ft=-S>q%IA&nbMK!OCTA}__ z6sAqnD0|$ndLom(*^TY(I?OJa3u9RPNADm`-Fo}fyj`x6bXK-I{KBySw0Trs;4uI| z7m~V1nj{~Nm28U6PVxO~1+%yJ_m=b`M=Cd`#>S$K>k)xP@n;G_<9A}=5@_cfZwfPU z4EKH!|9+)u9V1WGM=&;Z(h;rpq#L@E^=3we#g!=hRiyM)wZp_GU3&hGKn+wnKmnuK zI5YD!!J?uaJ?6}(F6iKa7t#%NfYf~cbBQyRYR_&WB>={yO0*1hPON6P6FewG&n-t= zkd;)8c9l1Fj@kX}t6OBM)|8$SUzos93qLn}#yS^M^WUztX$3y@UX>(-PLk90$j@z? z7qT>>9?{NmogrhnK{qg2>WPFmU7Y>%1XT9OZAjK3)R_zTB67cN&y8bwfU+f~65enc zzS{(Qcc_!&h_CH9VrWBNJ4hOLhz| zc2M8jYXRnz@@hj&^yZTc;0PEIC{@Ux!f3WtY;>legCeqO0+qResm5 z;)mnSh!yh_2QwpPxA?yQ14lbn!&K0A-0RS`; zfM%iB-9G64oFei#VDq?UIfK$fjdb|7ZgrZJk#Vt^n@WxrQGSvNlZNg>+Hp_ePR1}r z!I$$UJS}-dLIcA&fMY(*h4Zq?6`^{@d7F~8vk!EEeq-!7HM!IT zRMo&W*>jH@v1RL6do(yvFVi)n#r<$(V6L$th)W8z+K??oHct}#2-?RMprgnbKI62iW?%y{oTJ<`RNFW06AkS*PWD>sx7D}I>) zjnCDj&N9CzrSZUGrKmz7OCH}!__EO;AsZpi^t~5#lr&Vy$u3~ndJ1(%_%htcTCn`YbjOBE#?14$ zF6-MR@zFV=Xfz9rhsK#d4{QM-867c@Z%A^ogH#^wEYOghOE&s;O(T>qL5lpH`=MyO z)@}nw85SV5tWYNuBZ%dCi26VwlMPXT@%CMVB`wAlQ$uCiYaH|Qp=U7STCVv?s&93F z4owPOw0dw4Rmi$8w9+EZ2i){g1}zlX&Uad8=ef1ju{PPCVDHWHm&_-*2rA|KqB`hc zY}>)?^^chCj)#{&?0YHkd__w!=``c`@u)Pmo^Kcunk*oidko_J$%*ywoC@C%3dAp- znkmz-N_IcO+0VMKpNvkr5?4LLBz@Wy<|lR|@1OhOb}S++;79=HRzG{Ar%P~X7FG~e z`9wUSdbgc^_%0uM4{a>U1ifBSME0jS1X6}yxIB!I`<-YtD*eeOya^F_cc#=%|BcI^ zzv$VqK9iqhu~fz=l_e+!Le@p=)JOw7gD?c32*40xX4{Kc1yvC^BIN!e9&pROb#pI> zogJ&5I>S2`!uL?*NJ%afJsJ*Ql!tGSiYi=+`!b{fnXcOF1yic$BC2W!`Rv$f0r{!! zCf=dLI$|T+jC1(N6v?1NL|6}5CIOnH7aj>RjCB5K%eC{bvuzCh+&{UeJbOq2q>g#W4gq1+m~4b=#jjp=2d{E*yX@uF%ODM8<=Q3|96+w~w?W<;r8AzI78%j&xv9T+ovVL#e$_oBK>j?*gD}s5NK~FP8(E>?9kbZqd^}EbnlY{R6A*z%k6n0G+I_pIa8DiNw zNv55*Y8CmlAPFElt94kf#5 zF_w;ABe%9!x<)l=l;oWiHMH_d#f-&?hlzXGFEFaF&%g^3 ztm+5M1v8F{*CZpAf0Y6?5^hMkbH_m58yqoIIy4`U)qMelp1oM7ri$FKD>A1t)q1aj z4b79ul1%9C7pg;>9mbPaXbgsmk%knGK&}w+Yc?zmu9lNqjI!Da{#F^81FQJ`N7yYfQ}f@Qptv}cuJiyA1a)-k-qw7y znWqK&jtLm};Y{zu8~>KY=jWv-maKNBPA_%JBZZhpm#>!fga}(N z4ejp8m|dvWvZk-Ylwmt;M+9i-1xN&n@CQ97$5}40>M#xOjc=XCGR`j8su34v^GURM zx$b&TE?ROcIC$(Dx8u4I!9m@Tbkx>3-3D-)|I%DWY@T|kEb-H0lAyCP&$mUs$(-+< zR%?+v;vEJW5Q-J61vHU3%6J5k)M|SeRQqghRTd7Q)~MlHk)9~P!`g27jyR-VPU#M( z4}Q{=Rz%MnDP#K4Y;@jVqpNFSd-eL9Q#HtA;Nb|a_tCn*Ak4v(9kE~O;J^k2;Gk?RFE62OWp$U z0xf>ZX^mtEm-aw7>8hqE084&f0qTV9;ov9W)QZQ9xa2im76FsoN@oSw{SrjZ+k+^fLN zy1yca1lB_eDo{*)a^(MsH)fg3wtJ9k-xHo;`OJFu@ALBq4l5P zaZz%#&M%?W%=R)L_tL=nya*}u!*@C@Z^95=Xpm|GWHDY)%j(~L*$Q(<|)vF?1-y?K%u(QAFc%Q4Vu&Pd1V%VMH3DoOz zU9@HyL+%Z8R=xgIb`_N7i0ndeP23iAn31;Np_tz^)?UvlSFg_-FnK>g$6GB-8(vki z3g}wE5GGLbF7=$|bq})Lv|@fKXdR*SJ>Hx|y2QqfG&wJ_1+;C1Z7Ji$W5VnYb~u=R z7fHf}Yh*Azi#-6w<+p>a^5najX7AxXewh^cm<8ev;C3iw397F0rqsnxa1|Vtw!oz- z5S=3zX6Rakckuj?{;JI@^SQbCLR)2*TMgEuv^)mmgz&o-@yHOKwM+gI;2BF_aH+qR z4zP5djFCHey{=xgV1j$>?~|;h|23P}w0fEhoCZz0i#9|E{%32bja#>mYOmx6n?5Wv zHwg5_%b86j)$4KLeNU7+te~A-MG{kbrHFSSNTz@^YbroqR2sq(4T)SNO_X3mlzXB33D5P9Sbr&z@uC;bYLwQ*kqABnm2ac zG|fHpG@7Z;stE6LY+Ps;n;#%)2@CE+YaUSJfFnejXi>^KF1J^%b;&06cKhsI=L`~mxm>ddmlSLQ!5tX zLzOV+`5$dH#H{|+Z^q@!`}AjXLv=w#?db9Q`F^Ijd-ktH(E2KD8#x)=n+{2rQMv>i zsp0o+ccFK|tEi5Z-AZtbezHuYRC>)&7rt{0c&hu7bYOuU`DV@oj^C4m5=|Nd!O_oC z)u@=2)<@mS+WxaL{Xx|XsG9e~-Xcpf!mtae9k$P5^QD(GgRc&q?F^O;0PLu@5Bmor zayh)KZ%=s${51xJ-&W(D6{i!7%Hs!WHGLyFdhdfxv6MO--b;;kbV(bwEj%n^J8$>2 z5^YuG1dZ!kJZ|$+a7&sQ3{L2(vLekFlmu0+z3Ek@_iQ&qFFFaAKPxp3f~T&Sy}i_C z|NN!Po_$%YPEjI}N)ePki{e>T@%HMLN=pOX*1;V{4{aOg2XrFO`tc*aEe1<)$4;Se zUK+|}u5goQ^RAJs{>`yxU&*|K*@hVnkR$@RkQgx=nx{!_l> z9@SYxdb*Na%^j;{{I1@!K)NTB>Jb(Svm#Lgw;m<)sqP7EI3KTV{&j6(IQ@i|KZYI7 zI{l>2d70r`s+u%!@Gba|JKwG*)+y-W2WjBwa(=*fBFEbE_}K)LQ7V)MN{)4}Zf{;Y zddPDxWCcCGXj9zE5ny7Eb_>Gzck5Xj>O)`iOkzUPXQ8h(Z-(Y`tNv4!Yp@oo^w0;G zCnMwS9cd1JU_;@FAE=ItLnwcSVp#JN((v7$ROz9Oz9f@x#R**|I=vwP#bVf6(s8|+3Gf03i#Eivf zW(~cNn_B4k(Z3PZ$$8m+vxS0{MhmW9EWWj3)qYFjrw01&OWR?N3s3IT{Fd-hXzVG= ze(|<S4TQ~-t;)6F_m$Evgv*IZ5idHtQz1VtCfOC-kYTrQ)I7eX}-j)KbF zS{3;_7H`1b`Tgnl=2r{LGRrsi9r24=#zuVBGCnzBqw6Yw76VE@;RfEk0=UXfTe1(5 z!phKY{bCNnGmjMaprZ}VLF^Oljfl3Rc99=Dz@CpvkYe9mv8@Q2o(i^~;{PW!Y*JTCp-{2kB+B!y#Ja;?e0Up^lJY^8~);*RSRI&&$hxbR1 z)2utc>!d2Op_hGaYxacI%k9C$9y7JX~I~9rq6M;L?b1`_7dg_(oU7Xe1%=_8URLx(i4s>dxUU}RMe8oB7K->m4GUg{ocRtpcX$rMgwYb0XQ7twKcP&E}rWB zKIPz#%VnKIe7_nx^%)3bJdE9lHd;GbY^F3ZX^=cT_t?s}T_6q?9Mz%4^6)EkfRI>T z)Dyd?ndi`J=vzTd%=dFOO6Hklo_-i~nB_!JEx$xUiTXkf$7Fg8>(0PtycTAtU5SA$ zfV|dOKeV)@jseq(uLSHLcdiOB3Ac~FVe+04nOPYhCFheUTb*OHmq)a|sa?<8;oYV0 zHo1(Dsll*0*>w&;yMbaGaCW>9aE&qrP5nvCV-1$iJtR z>C6NPinoQo+DmvR=0>~`ww14RpQNunCy*;TWf5~X0_6j&R5S_X?rN%jt)5)pGs%V> z+}ddc@zQ=|lw3F-w01tbFE9eF(C`xKP_FK#vGbo8`nTIl27}+?+GnWQa5CW7NhMGv zSh*tLvWnw-EjyK9H;h|~bI#gTdSPptP?_q_wQSXsub|on2xb#3FV8o~=8u$G;wSjF z^_yo$*ow3%1B|d<5Tp96wp$|BG0a+RYmND~OZdag%GW?GyG}kZyKtmuL%~V)slj)* z-#5_Z#}C+FsuLr0syaa+Y&Mz1;O{vRqVcsgu6ek+^50x!q}I?*KwACYX^^u>&z^aR z5(Z-^f!+J}`1&pwY6`U*YjSSqogf|TE+iY5Fd`@XyhPD=Ca(3F{aNYiZsh=8X60?m zH3u7?Hkk1AfOf1xuIE^BR4&C3 zLeGM~`R;@`QCgW1fMoBUPV)`cnI_k>jjzUgyQ*%>kJ58STAHV-BjXf$RWI%C>9xul z9LlI{*guPDigzPIt+9t;!t$^``?|b-6(4>aPtvQ+v{uyU@i_PH4 zn_L0Pru42mas7z->|rbuijKE@L+C#MzZX;kM|22pk$Ih?`lVUyFiTS&CHLpI2U;9v z_ze;3k^ENpl~h>0ZfH^**$7Mooml)oyg?>CH7XPSw?OtT8g`AJ6quu$_#|2=k+-I3=9 zjt4blEBw&zW#P}6;9|K7{lmHvl;j6hz)er@#XtD zsM504#jqoCr7N#)THu{_73Flk*<3%YX=DaXP`<*E#}8XB2<3SoFor=Gskm;3x$M^G zCuccERRGrWeW@ifUF$TWlXy7V`6dW7){!#+W6Q+fSi$?YaAOZ}rx&F$wbET86}1nL zn$o^Iiw9aax8*iEymLjsOQaXu6G}rCKDoMveQ@!s%9bi4iQUBx&b*B>tm>SO_gx}G zVu1;^&K5f9jhJFRJy_~8QST*-iG)^|@6Ia&u1?$rK9|)BdWV#|=a7t5HEKV8nrBag zeGh0f)y1cqvYCT5+^@iU^E5?ANuK~{BZ^8xs8O`iZ15**-%6I=0@7wmT{}#|J^CS= za{f-k7QFh*di;8p?gO9sSyJ5zf03I*APy*CTUkrOll=0!_KrKRiNmCBZ%jQ5y!)*B zSSR^fHf1_ckbCsIYrolMHeS0Gt$KWl&K`Q(fzA{iPf^M5qMgp*u?+?c)KqLPdY^^UXzLYh|-FUfqr+S;_ zxZS>>zA$GvzRqp5xb9^RlGNWkl5CB6TG*V>q#=>-eBQN&-&W=WGdh>IXTqnE@hdEWY za+d+y8mcS|fbK8BOgcbg^f(N9kTyq87!liN{7;}cz^vbNK5&QO>nd0xlG6eFI;_HFVP4{{3{q5OveiHQvg)UvkW6jAsw>^1cRoPp?ZK*^TL~{K);^us^*|R@+=0 zwr3;5ZI35-_q1**GFu~T#rvP}{5^j(o(trnA6-jr2orR-)(7>go?U@sGrDd@^M-h0 z@=#`n%-et)<8q8YiGr^#F93T2G(IB!63mMbfsFE^>CF?z9mIH}xcWcOk&-!*8i}YbGJ>Y;TX;%D$ zXTk*?d9!`i%iQh7GmqT0!{aO{zc3NOgBK|=xiUXrVtT%Or^=qjKcyRdB?yU<5hSvp zTgoF2%iXsyvwCxp4CU?gpqqcr%Zo(Yc*1yG#{uDg8TkzNk?Wz@ICw3#6w9q_Iym?QG!qwiu`5Y{V{SI%Ci* z$GZCGI83h!ESQSR9jyOb7Fysv`I&;W1?2i~d2W)ac$-Co1HChxer@<-SyeYSAcHa< zje(=6!+vH$>W$U7DW<9Kj8Vscp%bu;9-FG>OMpES3bXz4DQxqzt9PM(iZ>x6XpRKA zLXMU&D>ou%fL}Qq3yY3I;`+`Ks_m7QjV1j(t5ouJlVmgfX44tRh?KKZ!%hLg zbdRkm+NFB5*SY!)7R!(qNTPp^V=|d)uVOsPAOZg34vf#vW*n#gP(W@3u@ zYa;JRno~m^Zsu03!{EG!>)>OMF2o7fKoEeRE~LK}|6z^ASy-TZZ=7^GY_bi^3nm!} z3WINa)K0+sLrM1*pL2w<72Z` z4tv%QhaVc9iA-l~&U+K5>cZMkO^VYi{Am<{jzUK&b_`{})>E&L}v!{)nqJ9BH?!RRAtab+HlgphOXhq>rT zIazY1h9PWz={@E9L(F|eI1U+8XU?b!j)}*j=K`(p>5c;(tn-w}Uq}tpGBg^4!wsv( z>8Y1Nt9|2IY09c+1Yv}hWXr~}_drKR0QN!$5f=?=jdl>6NhA*D2srqY_i!gX)7-Rr zOC>u9@5ve}nZ!jt#2D8U4^iVQ=1`xbhRbykkt*#$s|6=vQo9C+ZQKsAqi$;G4*)AM zIJpG=1U#I3g?6$U{yGG|Y1Rn)(sr{_Z$7~+!CHUPf->`&VPh$d5oBU!JiQ#ok_iT zSRXk)R`H95N54Fcn7KmIDa<=2E9RLe=wcstU@E_C7{p_`~ z)}EZ)-n&ZZ%c#1`(KN3kcOIo~7S@^k?+YD5y}8ah=&MEE>Yvvf9uA_mm=WXaX?K5V zHuZsS5CbCFX}eO$Fm3B3(ULQ#IX5DC&YcG5SeGO%esZ4U+7EsWesY6OlGl2k0(X>e zFxvTgyUwvh%on1qdboPL)o)n+Ux?G6PW`X76cuM5q%L@&%5FrYJkWmCv~aHGJxy$< zXlP-c=oY)ZS6Pwrm!YG^51>x}2q)MQ3#2(v@mMhguQ5Mkqw%81unzOmz7y_(rQiHy zva>D$xypa0()vQrcfN4yz~{5(Kn0arAv)BfUd2KBMbar&>eMIC3pNl;8tdISH{yPK z#aDQWgt8GyAE*Tjk%a5fKSQeunhOMq4|p_i_c^BvN?RM*Y84D|X$H{7IO-J`@BmE( z_DPKN;eoIgzu5F&_h*>x^pr8IzfPmwU8JR)Mk zGt~p>i<&;cdA_UsrkWOgV&=LHxKtlxHUBcdKYX^1ey}!#E93lY3YG`lx_s5x*XKI1 z{7M@;mj5PjjaCDea}L(cskeQbbXUOuy4|&%RT1I`%%*$8ksTp}Zq#y*g4SXhG8^-yjVpmHMd0zdq6}idWS2lUT~Gq=pWOw7h<~2sl%{LcB|% z13jE}>*$?!tw9*&Gn8ZtDKw4ykrry6ggX%0rV88E*Ye)I+`<*h2Ry4Bb_{X%*U|hL zJ+`t6eY8+q6$i#2ABWz5R~SO4BMK7m{ob0s8fwHIwdG?<>vkY1wyNW>2vDDjj^NF9 zU)RgjaG5Ln(TBlZkSBxe`|jN3*)AP-rJ%<%_1Nw?`mXV`(oz&=Z}-*nx`!H?C`e)E&lg{tvi09=1NXWm*Fm4?T5hUtS9z$+WGM*p zd6FAu`*u~Hn_~~V%w@L!tFf;DigQ`oMuG>2;O?#o!QI`R;7)Lt;32pN3GVJraCdii zf%&{-%nmh3%g1o!Ope=AC&uMUIwS4fCOA#=fLc?ewZUNINO5rO`hr z?0^F$0u1ayMheZjV+2fn!dH4Bx^dqCMW zDvk6awL5C_Ewh=rStMS=WnxJLO&=;Ud@{AUb)Fw;#wg5GkkWw^eM1KsM-f%}od1 zTMOh(Qdz230>nK&Fc=XV8dulEt5#zR2bKbYZ*tc3o`TD_9PNI3HLQXGMwx7BOefvC z`irmK$v%-=)As})yTu82off^$YVKPsVsP}g^V5e5TXZv1)pOtR=$cykK2c_6_ogp4 zJLoFQGtsVLt?wLoQLjohj$v;p+#^DWr6PwYBuHEx=?=0DR)?)LYox&&&O%_T@bWNy z72EW(zYBgipc#$~-suLa`2=V|;T&Zc*#fTRF;)NMJ^3abq}f+}uu)e8i@7n4%0bLR z^X$T9+S5BDV-(md2|N%Bq@q9Uyn8faS;c+1Oxj?CzWL z<7xFA)QMSvvXyE;Ee2I-F0Zr ztP%mMl5)|ybuKrGY;|~<(tJ8Xr!rnfH>>uT*Z7*fF`PcKF|;Y)Y~0jN(yU;&c|bg2 z%nKUbksMU_YCqjAH^S>HklQ= z`5wtYHC8i2f>o-~I;MO5OHw?O03T8paSI9lRCk%f#rjz8|W#`#kd+OeKBb7|m6x`)9C5TP&y8a+^LJxCut zo~)m-qLnagB}eDn-8pZS_=*fefZ`Tgd0(-|>QWxjsKnwE&{b@%#2d&alp)yCPLiEy zGh<7ELlI8UO}x@eB`477daoYfNFWYwUWQLFm7nQEKhYdnD--T(@mf^oqE;#_vk3)? zk&FScWDb2ASw~x#lh;()$`8hd{(-Ezu}{V5s0*%K!W_i< zTOZQn2AJ0RBPYQ&8%WTtty`hyC|+L=T*83CkGYue@?|ol#JFkkM+b6;8w;xz1$6m4 zg7@h^qLO@VRC_~IPu)k)U4K^j<@7sl<)PR!UNQo9nmmVdB72Ojtp|dP2kp&AFd3>&EI2doiID84UVnl%K*# zbJ+BCaot^@tSL8IA{{B@;A#~?%JRRHs$0?3Cs{J40&_^050Bvfs7y*fn6>~ZLw2|t0F+VJd_hy2?$1+CDUUv2e94y~ zvmhgDIBh`pbLw1!G+F}nK>Xad%m|=(An?Lja-?1e-xPDYq#CSLEES!XvK;%w{~jGW zs#?-SPHVeebiTHd>JdX&KM6$>)F`6YdKp1`E>S@(sr4kL&k&ft}x=NzbWUM}B|i}2w{ zg#p$oGI4UZcgNs1jF)_`zXmxYr`N`@5Ck?jBkR^BvLuzr?(=ZcDed!w_F#dq5?XHW zp|gjBo`U1U^G!;g58eck3F~fG8V5SX!8o#5IyXw zRA3Wu(3?5L+a>iouQK5h2bL@Ac_0&5!7TlN`XsnT8|ElB8PbvHnJQd zFd_Nc$W;_EynTTv)|=8<6(wx98D#a?aiqB$ivhH%L6P~;;l`z*9bcV#&QnLHsEKU% zH>(B%Tl+}6uB!e0V`>crw^bLg%Bv z;wj&fCLIFZQ*pz(g7lDuV#`iEm8f~M(#mLLd|EUVK^Wp%cn12H=C099FxSIJ$-Y5h zQ)IW$V0%VSj0)|Oe`OzlfcUwjj&=FP-#GM9K(@bjZt4hK-c+eoRsQ_}!3{ZNjuGe2 z7MxZ@sjt524xIRV1=cYS+jJ!3WoX6d$jgFs8`o2$-wWQ=Cs^W6tzkl2n5%mF$XeC6 z!wY-}7l$*-JCV6prWxCAvB%{)^CK?QmcQbeVA!}$he6AwX2?)kh+8*>Ei31oQyOox ze$N^o=Ac$Po}G5*Xm8+BO0Iwh8DRaDv}cw={#`ZwCrt&5V^m|&3XAhE+7y%KKUC!_ zXc7A331bLkl99PfcTnGTZy1N6=hTpRt$Dh$a#JI?rTX^M= z)YIBRLqvy;!+aWTW*INxKPhvM%j*DhsRva4BN1 z^>MjX*BkM|>Iz3__c4&EX)}O=j=i-bYT1nCGMee|hulr+x%oKZa=nNd_x+}`;Ns6| zTHlMpgd*Tq1>Dtq(>uj#aSVzc29_KkEhx5Hcju_`1D55QW-! zs&}!3-^D%cKMox^-Jppj_peEo)_e=R5bpx#n+VTg-!Xs#$3?UqJ9s@a*k%5 z9PiS&!|1HBAA?V+V#PFvNrP=NGSJS(_`VYl9D%o+4BoRM+oTzPoo=;Mrk{ChSju2C zpVCS9@X-8pyVSkfFvJ@Q2FXhwtl?ki4=Y1;LC@YR1 zs{$T)4YEFUgG27TQ;<6XN*YS~dcg+VFMf=bf9_l5-A8>}kx+qKP}Z3^SFHOBw!rPq zQ4C9UxprYSYn%h9G)CbBUHNQ96mH7#j{{d&i%=8`O;~QssLNX0KirHS*OTtJPZpPf zf`C(1PUVbw>Z2YIoymCFwN}39Z!SmfhAQv3O&yTHCuC+bk_>+{#1@?~ETw_$o`E^8 z(fBZ$^pI8D^bDy_Gii2|0L($V|H|Tr`ZuJuGd8d=pnLxP+%X5Ll9u!D(3_5w5$bEo z&HL@W*#!4z4yLkUk!twEDh0#~DP-|?Gn-%!tXn*F7E9Zq_p94Wi+Eu6$Le7v~ z41&56>W)$cs|>x#@-PES&1y703B?{$8=rmo9#oKr%;erL+LZ`);DaIJ>@fG|8jozn z%SV?tLwp~=TaqCn*vw|OQV;C5+1|={)D<@<+nNnBYKkRD8RbTCwBC!!R%kmd+qo;Tv2AQHvWY=o-R^y?Zv{i36^kPOq*I z#M;D68Uu5ng@wYbJM1=K3;Cj=AedHTkZ4gA*zkihnxr$Cdl!%@A{(X|tRl+x8_eIt zMDUG%fpP}5Ehcij%0ycuXSk7Am+AH(Co2_Tm|O4Lc}3?a!sgGmQd_>@NG@x%cQDTk zIS51ioe4EN7Qb;JjOkq$F)eyi$0TBiG~q`fue#yHS})2EZf94Y1ZL-te=?pAP|7N= zD(%*F`$;c*I4re}JB9J^Hw6E4P7x7)^^9eR%NT| z^AsmVBAA4W)uD}9OlS`J0$4088YX-`gKB9J$~j9Pb$H;X+n*eZJc)D4*EM9NK0L`I zT*LnvHWDD&$pzk=c>Uuv&sSK`oeNmd(x3h&`~?qw{|77TIm)$S(DDK zM<~J7!SCpiKDvw5f32w@Et3lET`ep(yM60B9_{Gj(a=smXx360Bsoib?e*|kkYJvyI6Y@$~LtdMH<3VHoUnPCZ4arvgOn8^`%j*n}p)!r&CI71;K`?7h*&o*a<)T z!j2zWWSyv!f2$CU2zXti#ymfvnW26B=EyApl|3b@211;Fuwq{@0dv3>jB{Np@5!r~>$tLZ9| z`cMn>vM556ITS$z{NDOh3bAy_2oTX(`@?7$yIHM5ez8+Jo|Sj13NXmuRvVoy4MkFt z@5zbPZ1nt;kR|FFgftkEAY&wt+wT>Pd?V}?NEA#v$(#mhxDuP#HACc$5MEKSPa$hy zysf6*2CGWqqt7Uk()_%uTR5&jzeHX8g&z4EyK-noF4T`7kyOW7b0`z7Q~I(vHhHRw zR-5Ts*zknyB=$U$Tj`ZFbEjE%{e0EiO5OUJCQ;EsOdn|`Qp4?+yrHYS2u={aIfqEo zIY@1FcTCAPxgz@K>I5r?@mZMbc{1{}cz`*O@KzQ#D>u|D_w9{SLW=v|6lr(o(&VZt zWR<8u;UobjYc|N`NW%N+GPc6m=GB7%c-Cun&n56w?e#qg6MiEkT10wYEnE-*4!;Hk z{}grPSoWG&?4mAlO0ui2uM%rLuI9;=hNP@-(H$hjVQao9N!?md5<8W&O}~nX=0$8_ zS(o|LgL~`4Y>&N`x^m8t#!|NsG5q z6HAb(2(4++F%%O*meA{KmmZ}WP|DGVjWyqnKKDnYk@ucSd47cvNuZ z5&~m9me0aPn{9{l>5^}@U9nR=Wld@~4SP?1nw`gu3S`U&ouk%YpCd=0+mCP>puTNR zLAa)O3G>%(LRIo<2d&4P^4Z#p#G;r0iN~MQAbW-PgUR*2NofSFbCD$=U$y3DS!l~n zi?iHbZ0*e1z(~P!>|?f(rs+kkF#E=1fj>{gEyJ!Ocm}Q)m4m{dtCJ%%x1VT<(eZ5Q z0n0jY@-E)f*q$}y(oF_%w>Coj+t~hx z8PV3j&d%hc(etKZ6@Y8Ho&NRrGphUrRTw@`Qnt$eR{>rfUGT@4jup`zvTKZeCEa^* zJowhU3u|H9+uL6Y_LPqT(In#EPi*Nc#Dnm+$F|kK8}6Sy$)YIdDP4K5ZQ(Mo4=rg4zg)7irNn7*HTw+0c2>QI3qbUDu3>1uM zG{SPAX(p5?8{B7yP0^rFvsGT~+gA7#%4vAn4BxgsRDqwB!U#<5oQA?zS0KAK7WV~h zAX$UY^_g&&u&}p~r`Kxxlg4^LC2W_fY7s=c+vcOjdE!6$vvNqSx0HmSDe5fpT6uF= z!;`G_Y=+tTc6kRJ?uqsKOdVl!pUb?4|N{t%z-e|sq(P*{Nc2D zy^s8c*Iou&$|(irN79iSn=T^N-Z?OsqSo$orIf8ZS{_4L_$I$cQLH-ar&VMEqV*^9^k6>o~C+ayv5)=QlwAvwZ-2%8{zy*fBv>qh0 z+9e3|O2ie=QFZ3q5HP19CVXA0<>iE(+OHN+~I1Ulr5<5|6NX zynVNXjVb+pTr6hxmv?}~LrW}B(l-8PGTI9YLEY*YufCfrVk?v%2faxWxld0#xd8>f z$7#bhn7ps_JTWq#&<$nIn`Ez+x{|8KsEaDV!K|R?Cj-8<4!`NjB0)Gqd}P>XH-6)Z z>WVP;(?a&T(pw|h<<4s?7@5?XcMCQZYfcY!&eX=)ql2AW(C+uwpb_Y>BRB@IZ=0Kl zPIWGDC0DP6RA7POcdAQqD;FjfMAC4>MFnDYZIck)a)DHz*<D2AyAX^-^WT(~WM|5JUpKQ_87V}Iapi94np!XYK%&X@r)eq%o zw%gQ9Ps`mS@bKi3!Q{xAz_@^^XPp~Ml%3k2D^#S!5DqEsq+xhOJQpMJA2Kg%`szs4^ zWffJfLrTQMbV#y*It?Z)nOc`Zb=3R5ER7s50WX6yjjrqHTydrqgOQsf{6klxoru9! z`EYSsHfS($hvZDXZ^(-p^7?S9GmobNIRn_c7YUgrYx8HVz3H4`!wG^xQ7NIL*4#&;D9^S`*tP3#OTevxng#wRVg$15P=ikQX{0jdZV05+N0>A1FnX_@}>mI>KKOI)xcNPhY&f2r%G_cSYWNmJ~`KPFebqcY7!GoZd~ z28)9TrI&7V{^%+fYh}eI{yw0Ys;qYKe)uLCOhY7Iw0KBIK{HSxQ zLk}IMHTVYSK6k}bYU5x~YJ10)XitAa1mtzvy@+B&e9IYE6S_+CbL8c^)sDr0xX=K@ zy)4kqo|#~u#fJ4X%}`_-B1<+M1H##oNzF129L6eZ|S5r$ppcD3}?uy)sTd+iGC%OL-2P$%uI_>7Kp~>?S=1ql3RY^&p=7*5FR*!5-zPh&%NwZ%NmR`{i%4(SO&dG)x^fCKxFqWQ;<%qn> zO`vyQY&f@Z){H3rZ0xnd<@Xh*3Acz`Y|YQB{->Q48#UkXkIh#ja`d2@oujKPTmnSu zhx8&lPW+jjt4T!VSXAfyRZ*rLbCca4K)B0lN=|##gRd;hf@&rB;Qa^J;YadQEoFbY z2mW{-2DD=L)kA=Y1^)Fjc^U@l{D6cEU=;tL;A~}JtNHYJxHDGLa*ZCr|L7i7wiuP+ zXK6lV>Sqhrh0PW**8&Y!kdN!|v-~ypmpI+zf&r3psaCv%>zD3^q(d|7^DLSMB`ZGV zhLK@`I>}y$N|Y6G8=tn|efI7gTo{s@*C-s(isAuht|@lx}i$*4g16K0(!5Z#}{ zY}Gog!e}eEufmfS^3uO=*@=+uc9c0)^qalM;*`>e+@2k-q)jJfbu5B{E zS2RX!!mNu~)J{_6?Q@Y-p+uv7l(sS~1CwD>dXsXb#KAs8lLwl4EqQPu#`kU=_mj0G zOkAbzRuRkuHN2oJyIOwW4=*C309(EwYVP!0 zvp;%_t;2WkX=S_gFaTazVhX%h@dnk zOrRQ%?-rU5OB7-}+%O9dtZ+dH3H&akV1a!6-P`JpGSYIGHgsr8FcTSm>}XYV;$2ct zh^X~KWRx`-HxMCI(|S@#l56z}R#Q?k4LKC+w)7+}gsmgE*-8_jk7@5=Qei!9ffKCEerjPu~z%Z^X1gz(|k;ObIgLz#u3< zziQ09tXuMg`+t}JsyOqf`pY64Pq_4#bO9RtNByr-Cx2?cEGO|a7=MWh00IA_{i`g; zp9Wr*Sa_-p@Jk8-bK}3%p!okcGz-`Y_%B1hrieccy`=U3-6L=SKzd>51*z}z{QRe( zmn8O2RRDg;3^!fMCZRZ z4x{l~2Kn7pVBt%F87DyBiAn|8p9a X5eEZMgaZM+0sQ0xl8~<9)2sgnN|qh> diff --git a/test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.conll b/test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.conll deleted file mode 100644 index b384b3c6..00000000 --- a/test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.conll +++ /dev/null @@ -1,174 +0,0 @@ --DOCSTART- -X- O -2020 -X- _ O -Año -X- _ O -del -X- _ O -General -X- _ O -Manuel -X- _ O -Belgrano -X- _ O -JUZGADO -X- _ O -DE -X- _ O -1RA -X- _ O -INSTANCIA -X- _ O -EN -X- _ O -LO -X- _ O -PENAL -X- _ O -CONTRAVENCIONAL -X- _ O -Y -X- _ O -DE -X- _ O -FALTAS -X- _ O -N°10 -X- _ O -SECRETARIA -X- _ O -N°19 -X- _ O -5 -X- _ O -MASCULINOS -X- _ O -Y -X- _ O -3 -X- _ O -FEMENINOS -X- _ O -DE -X- _ O -IDENTIDAD -X- _ O -DESCONOCIDA, -X- _ O -NN -X- _ O -Y -X- _ O -OTROS -X- _ O -SOBRE -X- _ O -5 -X- _ O -C -X- _ O -- -X- _ O -COMERCIO -X- _ O -DE -X- _ O -ESTUPEFACIENTES -X- _ O -O -X- _ O -CUALQUIER -X- _ O -MATERIA -X- _ O -PRIMA -X- _ O -PARA -X- _ O -SU -X- _ O -PRODUCCIÓN -X- _ O -/TENENCIA -X- _ O -CON -X- _ O -FINES -X- _ O -DE -X- _ O -COMERCIALIZACIÓN -X- _ O -Número: -X- _ O -IPP -X- _ O -11111/2013-0 -X- _ O -CUIJ: -X- _ O -IPP -X- _ O -J-01-06066606-6/2013-0 -X- _ O -Actuación -X- _ O -Nro: -X- _ O -66600666/2022 -X- _ O -AUDIENCIA -X- _ O -UNIPERSONAL -X- _ O -(art. -X- _ O -2 -X- _ O -bis -X- _ O -CPP) -X- _ O -Fecha: -X- _ O -7 -X- _ O -de -X- _ O -septiembre -X- _ O -de -X- _ O -2020 -X- _ O -Horario -X- _ O -de -X- _ O -inicio: -X- _ O -10:00 -X- _ O -horas -X- _ O -PARTICIPANTES -X- _ O -Juez: -X- _ O -Pablo -X- _ O -C. -X- _ O -Casas. -X- _ O -Secretario: -X- _ O -Pablo -X- _ O -Hilaire -X- _ O -Chaneton. -X- _ O -Fiscal: -X- _ O -Cristian -X- _ B-NOMBRE -Longobardi, -X- _ I-NOMBRE -UIT -X- _ O -Sur. -X- _ O -DESARROLLO -X- _ O -Juez: -X- _ O -le -X- _ O -hace -X- _ O -saber -X- _ O -al -X- _ O -Fiscal -X- _ O -que -X- _ O -tras -X- _ O -haber -X- _ O -analizado -X- _ O -en -X- _ O -forma -X- _ O -pormenorizada -X- _ O -el -X- _ O -caso, -X- _ O -lo -X- _ O -contactó -X- _ O -telefónicamente -X- _ O -para -X- _ O -realizar -X- _ O -esta -X- _ O -audiencia -X- _ O -frente -X- _ O -a -X- _ O -la -X- _ O -necesidad -X- _ O -de -X- _ O -solicitar -X- _ O -algunas -X- _ O -aclaraciones -X- _ O -y -X- _ O -precisiones -X- _ O -atinentes -X- _ O -a -X- _ O -la -X- _ O -prueba -X- _ O -presentada. -X- _ O -Fiscal: -X- _ O -refiere -X- _ O -que -X- _ O -en -X- _ O -función -X- _ O -de -X- _ O -lo -X- _ O -solicitado, -X- _ O -resolverá -X- _ O -e -X- _ O -informara -X- _ O -sobre -X- _ O -las -X- _ O -cuestiones -X- _ O -señaladas -X- _ O -a -X- _ O -la -X- _ O -mayor -X- _ O -brevedad -X- _ O -posible. -X- _ O -Juez: -X- _ O -manifiesta -X- _ O -que -X- _ O -lo -X- _ O -tiene -X- _ O -presente -X- _ O -y -X- _ O -que -X- _ O -quedará -X- _ O -a -X- _ O -la -X- _ O -espera -X- _ O -de -X- _ O -tales -X- _ O -aclaraciones -X- _ O -en -X- _ O -torno -X- _ O -a -X- _ O -la -X- _ O -prueba. -X- _ O -Horario -X- _ O -de -X- _ O -cierre: -X- _ O -10:15 -X- _ O -horas -X- _ O diff --git a/test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.json b/test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.json deleted file mode 100644 index 31684d62..00000000 --- a/test/api/mock-response/input/project-9-at-2022-11-10-22-41-09a5c372.json +++ /dev/null @@ -1,233 +0,0 @@ -[ - { - "id": 10252, - "data": { - "text": "2020 Año del General Manuel Belgrano\t\nJUZGADO DE 1RA INSTANCIA EN LO PENAL CONTRAVENCIONAL Y DE FALTAS N°10 SECRETARIA N°19 5 MASCULINOS Y 3 FEMENINOS DE IDENTIDAD DESCONOCIDA, NN Y OTROS SOBRE 5 C - COMERCIO DE ESTUPEFACIENTES O CUALQUIER MATERIA PRIMA PARA SU PRODUCCIÓN /TENENCIA CON FINES DE COMERCIALIZACIÓN \nNúmero: IPP 11111/2013-0 \nCUIJ: IPP J-01-06066606-6/2013-0 \nActuación Nro: 66600666/2022 \nAUDIENCIA UNIPERSONAL \n(art. 2 bis CPP)\n Fecha: 7 de septiembre de 2020 \n Horario de inicio: 10:00 horas \nPARTICIPANTES \nJuez: Pablo C. Casas. \t\nSecretario: Pablo Hilaire Chaneton.\nFiscal: Cristian Longobardi, UIT Sur.\nDESARROLLO \nJuez: le hace saber al Fiscal que tras haber analizado en forma pormenorizada el caso, lo contactó telefónicamente para realizar esta audiencia frente a la necesidad de solicitar algunas aclaraciones y precisiones atinentes a la prueba presentada.\nFiscal: refiere que en función de lo solicitado, resolverá e informara sobre las cuestiones señaladas a la mayor brevedad posible.\nJuez: manifiesta que lo tiene presente y que quedará a la espera de tales aclaraciones en torno a la prueba. \n \t\t\t\t\t\t Horario de cierre: 10:15 horas \n \n", - "meta_info": { - "id": "dump-20221021", - "path": "/test/api/mock-response/input/documento.docx" - } - }, - "annotations": [ - { - "id": 22, - "created_username": " rbarriga@collective.ai, 1", - "created_ago": "1 week, 6 days", - "completed_by": { - "id": 1, - "first_name": "", - "last_name": "", - "avatar": null, - "email": "rbarriga@collective.ai", - "initials": "rb" - }, - "result": [ - { - "value": { - "start": 594, - "end": 614, - "text": "Cristian Longobardi,", - "labels": [ - "NOMBRE" - ] - }, - "id": "GT6TTD8eMf", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 594, - "end": 614, - "text": "Cristian Longobardi,", - "choices": [ - "otro" - ] - }, - "id": "GT6TTD8eMf", - "from_name": "NOMBRE", - "to_name": "text", - "type": "choices", - "origin": "manual" - }, - { - "value": { - "start": 532, - "end": 547, - "text": "Pablo C. Casas.", - "labels": [ - "NOMBRE" - ] - }, - "id": "2Oq-A5l8yH", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 532, - "end": 547, - "text": "Pablo C. Casas.", - "choices": [ - "firma" - ] - }, - "id": "2Oq-A5l8yH", - "from_name": "NOMBRE", - "to_name": "text", - "type": "choices", - "origin": "manual" - }, - { - "value": { - "start": 1152, - "end": 1157, - "text": "10:15", - "labels": [ - "HORA_DE_CIERRE" - ] - }, - "id": "1bfxq1oxoG", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 498, - "end": 503, - "text": "10:00", - "labels": [ - "HORA_DE_INICIO" - ] - }, - "id": "3YLgdS7Hu7", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 453, - "end": 476, - "text": "7 de septiembre de 2020", - "labels": [ - "FECHA_RESOLUCION" - ] - }, - "id": "-HiUTvwFbK", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 327, - "end": 339, - "text": "11111/2013-0", - "labels": [ - "N_EXPTE_EJE" - ] - }, - "id": "2zZNLOfntI", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 195, - "end": 198, - "text": "5 C", - "labels": [ - "ART_INFRINGIDO" - ] - }, - "id": "eIl-etR2ye", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 201, - "end": 273, - "text": "COMERCIO DE ESTUPEFACIENTES O CUALQUIER MATERIA PRIMA PARA SU PRODUCCIÓN", - "labels": [ - "CONDUCTA" - ] - }, - "id": "sU9P19itW3", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 201, - "end": 273, - "text": "COMERCIO DE ESTUPEFACIENTES O CUALQUIER MATERIA PRIMA PARA SU PRODUCCIÓN", - "choices": [ - "estupefacientes" - ] - }, - "id": "sU9P19itW3", - "from_name": "CONDUCTA", - "to_name": "text", - "type": "choices", - "origin": "manual" - }, - { - "value": { - "start": 562, - "end": 585, - "text": "Pablo Hilaire Chaneton.", - "labels": [ - "NOMBRE" - ] - }, - "id": "7L82m_D2J3", - "from_name": "label", - "to_name": "text", - "type": "labels", - "origin": "manual" - }, - { - "value": { - "start": 562, - "end": 585, - "text": "Pablo Hilaire Chaneton.", - "choices": [ - "otro" - ] - }, - "id": "7L82m_D2J3", - "from_name": "NOMBRE", - "to_name": "text", - "type": "choices", - "origin": "manual" - } - ], - "was_cancelled": false, - "ground_truth": false, - "created_at": "2022-11-04T20:44:25.455645Z", - "updated_at": "2022-11-18T15:57:57.652671Z", - "lead_time": 228.062, - "task": 10252, - "parent_prediction": null, - "parent_annotation": null - } - ], - "predictions": [] - } -] \ No newline at end of file diff --git a/test/api/mock-response/output/mock-response.json b/test/api/mock-response/output/mock-response.json deleted file mode 100644 index 15a75e24..00000000 --- a/test/api/mock-response/output/mock-response.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "document": "2020 Año del General Manuel Belgrano\t\nJUZGADO DE 1RA INSTANCIA EN LO PENAL CONTRAVENCIONAL Y DE FALTAS N°10 SECRETARIA N°19 5 MASCULINOS Y 3 FEMENINOS DE IDENTIDAD DESCONOCIDA, NN Y OTROS SOBRE 5 C - COMERCIO DE ESTUPEFACIENTES O CUALQUIER MATERIA PRIMA PARA SU PRODUCCIÓN /TENENCIA CON FINES DE COMERCIALIZACIÓN \nNúmero: IPP 11111/2013-0 \nCUIJ: IPP J-01-06066606-6/2013-0 \nActuación Nro: 66600666/2022 \nAUDIENCIA UNIPERSONAL \n(art. 2 bis CPP)\n Fecha: 7 de septiembre de 2020 \n Horario de inicio: 10:00 horas \nPARTICIPANTES \nJuez: Pablo C. Casas. \t\nSecretario: Pablo Hilaire Chaneton.\nFiscal: Cristian Longobardi, UIT Sur.\nDESARROLLO \nJuez: le hace saber al Fiscal que tras haber analizado en forma pormenorizada el caso, lo contactó telefónicamente para realizar esta audiencia frente a la necesidad de solicitar algunas aclaraciones y precisiones atinentes a la prueba presentada.\nFiscal: refiere que en función de lo solicitado, resolverá e informara sobre las cuestiones señaladas a la mayor brevedad posible.\nJuez: manifiesta que lo tiene presente y que quedará a la espera de tales aclaraciones en torno a la prueba. \n \t\t\t\t\t\t Horario de cierre: 10:15 horas \nJuzgado PCyF No 10 - Tacuarí 138, 7o Piso - juzcyf10@jusbaires.gob.ar - 4014-6821/20 - @jpcyf10 \nJuzgado PCyF No 10 - Tacuarí 138, 7o Piso - juzcyf10@jusbaires.gob.ar - 4014-6821/20 - @jpcyf10", - "labels": [ - { - "text": "5 C", - "start_char": 195, - "end_char": 198, - "attrs": { - "aymurai_label": "ART_INFRINGIDO", - "aymurai_label_subclass": null - } - }, - { - "text": "COMERCIO DE ESTUPEFACIENTES O CUALQUIER MATERIA PRIMA PARA SU PRODUCCIÓN", - "start_char": 201, - "end_char": 273, - "attrs": { - "aymurai_label": "CONDUCTA", - "aymurai_label_subclass": [ - "estupefacientes" - ] - } - }, - { - "text": "11111/2013-0", - "start_char": 327, - "end_char": 339, - "attrs": { - "aymurai_label": "N_EXPTE_EJE", - "aymurai_label_subclass": null - } - }, - { - "text": "7 de septiembre de 2020", - "start_char": 453, - "end_char": 476, - "attrs": { - "aymurai_label": "FECHA_RESOLUCION", - "aymurai_label_subclass": null - } - }, - { - "text": "10:00", - "start_char": 498, - "end_char": 503, - "attrs": { - "aymurai_label": "HORA_DE_INICIO", - "aymurai_label_subclass": null - } - }, - { - "text": "Pablo C. Casas.", - "start_char": 532, - "end_char": 547, - "attrs": { - "aymurai_label": "NOMBRE", - "aymurai_label_subclass": [ - "firma" - ] - } - }, - { - "text": "Pablo Hilaire Chaneton.", - "start_char": 562, - "end_char": 585, - "attrs": { - "aymurai_label": "NOMBRE", - "aymurai_label_subclass": [ - "otro" - ] - } - }, - { - "text": "Cristian Longobardi,", - "start_char": 594, - "end_char": 614, - "attrs": { - "aymurai_label": "NOMBRE", - "aymurai_label_subclass": [ - "otro" - ] - } - }, - { - "text": "10:15", - "start_char": 1152, - "end_char": 1157, - "attrs": { - "aymurai_label": "HORA_DE_CIERRE", - "aymurai_label_subclass": null - } - } - ] -} \ No newline at end of file diff --git a/test/api/test_file.docx b/test/api/test_file.docx deleted file mode 100644 index 2a0e4c25f668f125922868b31ffb2584f631f866..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4302 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 RJ;X$LsZgqPkDdDM{{Sl)5n=!U diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/__init__.py b/tests/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/conftest.py b/tests/api/conftest.py new file mode 100644 index 00000000..22dd7a69 --- /dev/null +++ b/tests/api/conftest.py @@ -0,0 +1,140 @@ +import os +from pathlib import Path + +import diskcache +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, StaticPool, create_engine + +os.environ.setdefault("DISKCACHE_ROOT", "/tmp/aymurai-test-diskcache") +os.environ.setdefault("AYMURAI_CACHE_BASEPATH", "/tmp/aymurai-test-cache") +os.environ.setdefault("RESOURCES_BASEPATH", "resources") + +from aymurai.api.endpoints.routers.anonymizer import anonymizer +from aymurai.api.endpoints.routers.datapublic import datapublic +from aymurai.api.endpoints.routers.misc import document_extract +from aymurai.database.meta.anonymization.paragraph import AnonymizationParagraphCreate +from aymurai.database.meta.datapublic.paragraph import DataPublicParagraphCreate +from aymurai.database.session import get_session +from aymurai.database.utils import text_to_uuid +from aymurai.meta.api_interfaces import DocLabel +from aymurai.meta.entities import EntityAttributes + + +@pytest.fixture(scope="function") +def app() -> FastAPI: + test_app = FastAPI() + test_app.include_router( + anonymizer.router, + prefix="/anonymizer", + tags=["anonymization/model"], + ) + test_app.include_router( + datapublic.router, + prefix="/datapublic", + tags=["datapublic/model"], + ) + test_app.include_router(document_extract.router, tags=["document"], deprecated=True) + test_app.include_router(document_extract.router, prefix="/misc", tags=["document"]) + return test_app + + +@pytest.fixture(scope="function", autouse=True) +def isolated_diskcache(tmp_path): + from aymurai.utils import cache as cache_module + + original_cache = cache_module.cache + test_cache = diskcache.Cache(str(tmp_path / "diskcache")) + cache_module.cache = test_cache + try: + yield test_cache + finally: + cache_module.cache = original_cache + test_cache.close() + + +@pytest.fixture(scope="function") +def db_engine(): + test_engine = create_engine( + "sqlite://", + connect_args={"check_same_thread": False}, + poolclass=StaticPool, + ) + SQLModel.metadata.create_all(test_engine) + + return test_engine + + +@pytest.fixture(scope="function") +def db_session(db_engine): + session = Session(db_engine) + yield session + session.close() + + +@pytest.fixture(scope="function") +def client(app, db_engine): + def override_get_session(): + with Session(db_engine) as session: + yield session + + app.dependency_overrides[get_session] = override_get_session + c = TestClient(app, raise_server_exceptions=False) + try: + yield c + finally: + c.close() + app.dependency_overrides.clear() + + +@pytest.fixture(scope="session") +def sample_docx(): + return Path("/resources/data/sample/document-01.docx") + + +def build_data_item(text: str = "sample text") -> dict: + return { + "path": "test", + "extension": "docx", + "dataset": "test", + "data": {"doc.text": text}, + "annotations": None, + "predictions": None, + } + + +def build_label(label: str = "PER", value: str = "John Doe") -> DocLabel: + attrs = EntityAttributes(aymurai_label=label) + return DocLabel( + text=value, + start_char=0, + end_char=len(value), + attrs=attrs, + ) + + +def build_anonymization_paragraph( + text: str = "sample text", + prediction: list[DocLabel] | None = None, + validation: list[DocLabel] | None = None, +) -> AnonymizationParagraphCreate: + return AnonymizationParagraphCreate( + id=text_to_uuid(text), + text=text, + prediction=prediction, + validation=validation, + ) + + +def build_datapublic_paragraph( + text: str = "sample text", + prediction: list[DocLabel] | None = None, + validation: list[DocLabel] | None = None, +) -> DataPublicParagraphCreate: + return DataPublicParagraphCreate( + id=text_to_uuid(text), + text=text, + prediction=prediction, + validation=validation, + ) diff --git a/tests/api/routers/__init__.py b/tests/api/routers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/routers/anonymizer/__init__.py b/tests/api/routers/anonymizer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/routers/anonymizer/test_anonymizer.py b/tests/api/routers/anonymizer/test_anonymizer.py new file mode 100644 index 00000000..54a627e1 --- /dev/null +++ b/tests/api/routers/anonymizer/test_anonymizer.py @@ -0,0 +1,353 @@ +import json +import subprocess +from unittest.mock import patch + +import pytest + +from aymurai.database.schema import AnonymizationParagraph +from aymurai.database.utils import text_to_uuid +from tests.api.conftest import build_label +from tests.api.routers.conftest import build_mock_pipeline + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_return_prediction_when_text_provided(mock_load_pipeline, client): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + response = client.post( + "/anonymizer/predict", + json={"text": "Sample anonymization text"}, + ) + + assert response.status_code == 200 + data = response.json() + assert "document" in data + assert "labels" in data + assert data["document"] == "Sample anonymization text" + assert isinstance(data["labels"], list) + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_return_cached_prediction_when_text_in_cache( + mock_load_pipeline, client, db_session +): + text = "Cached text with entities" + labels = [build_label("PER", "Juan Pérez").model_dump(mode="json")] + + paragraph_id = text_to_uuid(text) + cached_para = AnonymizationParagraph( + id=paragraph_id, + text=text, + prediction=labels, + ) + db_session.add(cached_para) + db_session.commit() + + response = client.post( + "/anonymizer/predict", + json={"text": text}, + params={"use_cache": True}, + ) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == text + assert data["labels"] == labels + mock_load_pipeline.assert_not_called() + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_store_prediction_in_db_when_use_cache_true( + mock_load_pipeline, client, db_session +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + text = "New prediction to cache" + response = client.post( + "/anonymizer/predict", + json={"text": text}, + params={"use_cache": True}, + ) + + assert response.status_code == 200 + + paragraph_id = text_to_uuid(text) + stored = db_session.get(AnonymizationParagraph, paragraph_id) + assert stored is not None + assert stored.text == text + assert stored.prediction is not None + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_return_cached_result_when_calling_twice(mock_load_pipeline, client): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + text = "Repeated query text" + + response1 = client.post( + "/anonymizer/predict", + json={"text": text}, + params={"use_cache": True}, + ) + data1 = response1.json() + + response2 = client.post( + "/anonymizer/predict", + json={"text": text}, + params={"use_cache": True}, + ) + data2 = response2.json() + + assert response1.status_code == 200 + assert response2.status_code == 200 + assert data1["document"] == data2["document"] + assert data1["labels"] == data2["labels"] + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_return_prediction_without_storing_when_use_cache_false( + mock_load_pipeline, client, db_session +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + text = "No cache storage text" + response = client.post( + "/anonymizer/predict", + json={"text": text}, + params={"use_cache": False}, + ) + + assert response.status_code == 200 + data = response.json() + assert "document" in data + assert "labels" in data + + paragraph_id = text_to_uuid(text) + stored = db_session.get(AnonymizationParagraph, paragraph_id) + assert stored is None + + +@pytest.mark.integration +def test_should_return_422_when_payload_is_invalid_json(client): + response = client.post( + "/anonymizer/predict", + content="not json", + headers={"Content-Type": "application/json"}, + ) + + assert response.status_code == 422 + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_use_cache_by_default_when_param_omitted( + mock_load_pipeline, client, db_session +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + text = "Default cache behavior" + response = client.post( + "/anonymizer/predict", + json={"text": text}, + ) + + assert response.status_code == 200 + + paragraph_id = text_to_uuid(text) + stored = db_session.get(AnonymizationParagraph, paragraph_id) + assert stored is not None + assert stored.text == text + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_isolate_cache_when_different_texts( + mock_load_pipeline, client, db_session +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + text1 = "First text for cache" + text2 = "Second text for cache" + + para1_id = text_to_uuid(text1) + para2_id = text_to_uuid(text2) + + labels1 = [build_label("PER", "Person1").model_dump(mode="json")] + labels2 = [build_label("LOC", "Location1").model_dump(mode="json")] + + para1 = AnonymizationParagraph(id=para1_id, text=text1, prediction=labels1) + para2 = AnonymizationParagraph(id=para2_id, text=text2, prediction=labels2) + + db_session.add_all([para1, para2]) + db_session.commit() + + response1 = client.post( + "/anonymizer/predict", + json={"text": text1}, + params={"use_cache": True}, + ) + + response2 = client.post( + "/anonymizer/predict", + json={"text": text2}, + params={"use_cache": True}, + ) + + assert response1.status_code == 200 + assert response2.status_code == 200 + + data1 = response1.json() + data2 = response2.json() + + assert data1["labels"] == labels1 + assert data2["labels"] == labels2 + assert data1["labels"] != data2["labels"] + + +@pytest.mark.integration +@patch( + "aymurai.api.endpoints.routers.anonymizer.anonymizer.map_canonical_entities_ner_preds" +) +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_canonical_dates") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.build_canonical_entities") +def test_should_disambiguate_and_persist_paragraphs( + mock_build_canonical_entities, + mock_get_canonical_dates, + mock_map_canonical_entities, + client, + db_session, +): + mock_build_canonical_entities.return_value = [] + mock_get_canonical_dates.return_value = [] + mock_map_canonical_entities.side_effect = ( + lambda predictions, canonical_entities: predictions + ) + + text = "Ana Pérez denunció en el juzgado." + body = { + "paragraphs": [ + { + "document": text, + "labels": [build_label("PER", "Ana Pérez").model_dump(mode="json")], + } + ], + "label_policies": { + "PER": {"anonymize": True, "disambiguation": "none"}, + }, + } + + response = client.post("/anonymizer/disambiguate", json=body) + + assert response.status_code == 200 + payload = response.json() + assert payload["label_policies"]["PER"]["disambiguation"] == "none" + assert payload["data"][0]["labels"][0]["attrs"]["aymurai_disambiguation"] == "none" + assert payload["data"][0]["labels"][0]["attrs"]["aymurai_anonymize"] is True + + paragraph_id = text_to_uuid(text) + stored = db_session.get(AnonymizationParagraph, paragraph_id) + assert stored is not None + assert stored.prediction is not None + assert stored.prediction[0]["text"] == "Ana Pérez" + + +@pytest.mark.integration +def test_should_return_null_validation_when_paragraph_not_found(client): + response = client.post( + "/anonymizer/validation", + json={"text": "Paragraph without validation"}, + ) + + assert response.status_code == 200 + assert response.json() is None + + +@pytest.mark.integration +def test_should_return_validation_when_paragraph_exists(client, db_session): + text = "Validated paragraph" + labels = [build_label("PER", "María Soto").model_dump(mode="json")] + db_session.add( + AnonymizationParagraph( + id=text_to_uuid(text), + text=text, + validation=labels, + ) + ) + db_session.commit() + + response = client.post("/anonymizer/validation", json={"text": text}) + + assert response.status_code == 200 + assert response.json() == labels + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.subprocess.check_output") +def test_should_anonymize_document_when_annotations_are_valid( + mock_check_output, client +): + def fake_convert(*args, **kwargs): + cmd = args[0] + source_path = cmd[-1] + output_path = source_path.rsplit(".", 1)[0] + ".odt" + with open(output_path, "wb") as output_file: + output_file.write(b"odt-content") + return "ok" + + mock_check_output.side_effect = fake_convert + annotations = { + "data": [ + { + "document": "Ana Pérez denunció en el juzgado.", + "labels": [build_label("PER", "Ana Pérez").model_dump(mode="json")], + } + ], + "label_policies": {"PER": {"anonymize": True, "disambiguation": "fuzzy"}}, + "render_policy": {"suffix_mode": "auto", "suffix_threshold": 1}, + } + + response = client.post( + "/anonymizer/anonymize-document", + data={"annotations": json.dumps(annotations)}, + files={"file": ("sample.txt", b"input-document", "text/plain")}, + ) + + assert response.status_code == 200 + assert response.headers["content-type"] == "application/octet-stream" + assert len(response.content) > 0 + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.subprocess.check_output") +def test_should_return_500_when_anonymize_document_conversion_fails( + mock_check_output, client +): + mock_check_output.side_effect = subprocess.CalledProcessError( + 1, + ["libreoffice"], + output=b"conversion failed", + ) + annotations = { + "data": [{"document": "text", "labels": []}], + "label_policies": None, + "render_policy": {"suffix_mode": "auto", "suffix_threshold": 1}, + } + + response = client.post( + "/anonymizer/anonymize-document", + data={"annotations": json.dumps(annotations)}, + files={"file": ("sample.txt", b"input-document", "text/plain")}, + ) + + assert response.status_code == 500 diff --git a/tests/api/routers/conftest.py b/tests/api/routers/conftest.py new file mode 100644 index 00000000..c60de7a0 --- /dev/null +++ b/tests/api/routers/conftest.py @@ -0,0 +1,27 @@ +from unittest.mock import MagicMock + +from tests.api.conftest import build_label + + +def build_mock_pipeline(): + mock = MagicMock() + + mock.preprocess.side_effect = lambda item: item + + def predict_single_impl(item): + item["predictions"] = {"entities": [build_label().model_dump(mode="json")]} + return item + + mock.predict_single.side_effect = predict_single_impl + + mock.postprocess.side_effect = lambda items: items + + return mock + + +def build_processed_data_item(text: str = "sample", labels: list | None = None): + return { + "path": "empty", + "data": {"doc.text": text}, + "predictions": {"entities": labels or []}, + } diff --git a/tests/api/routers/datapublic/__init__.py b/tests/api/routers/datapublic/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/routers/datapublic/test_datapublic.py b/tests/api/routers/datapublic/test_datapublic.py new file mode 100644 index 00000000..69be0111 --- /dev/null +++ b/tests/api/routers/datapublic/test_datapublic.py @@ -0,0 +1,233 @@ +import uuid +from unittest.mock import patch + +import pytest + +from aymurai.database.schema import ( + DataPublicDocument, + DataPublicDocumentParagraph, + DataPublicParagraph, +) +from aymurai.database.utils import text_to_uuid +from tests.api.conftest import build_label +from tests.api.routers.conftest import build_mock_pipeline + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.datapublic.datapublic.load_pipeline") +def test_should_return_prediction_when_valid_document_id_and_text( + mock_load_pipeline, client +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-predict-valid") + response = client.post( + f"/datapublic/predict/{document_id}", + json={"text": "Sample datapublic text"}, + params={"use_cache": False}, + ) + + assert response.status_code == 200 + data = response.json() + assert "document" in data + assert "labels" in data + assert data["document"] == "Sample datapublic text" + assert isinstance(data["labels"], list) + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.datapublic.datapublic.load_pipeline") +def test_should_return_cached_prediction_when_text_in_cache( + mock_load_pipeline, client, db_session +): + text = "Cached datapublic text" + labels = [build_label("PER", "Juan González").model_dump(mode="json")] + + paragraph_id = text_to_uuid(text) + cached_para = DataPublicParagraph( + id=paragraph_id, + text=text, + prediction=labels, + ) + db_session.add(cached_para) + db_session.commit() + + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-cached-text") + response = client.post( + f"/datapublic/predict/{document_id}", + json={"text": text}, + params={"use_cache": True}, + ) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == text + assert data["labels"] == labels + mock_load_pipeline.assert_not_called() + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.datapublic.datapublic.load_pipeline") +def test_should_store_paragraph_and_document_when_use_cache_true( + mock_load_pipeline, client, db_session +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-store-cache-true") + text = "New datapublic paragraph" + + response = client.post( + f"/datapublic/predict/{document_id}", + json={"text": text}, + params={"use_cache": True}, + ) + + assert response.status_code == 200 + + paragraph_id = text_to_uuid(text) + stored_para = db_session.get(DataPublicParagraph, paragraph_id) + assert stored_para is not None + assert stored_para.text == text + assert stored_para.prediction is not None + + stored_doc = db_session.get(DataPublicDocument, document_id) + assert stored_doc is not None + + stored_link = ( + db_session.query(DataPublicDocumentParagraph) + .filter_by( + paragraph_id=paragraph_id, + document_id=document_id, + ) + .first() + ) + assert stored_link is not None + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.datapublic.datapublic.load_pipeline") +def test_should_return_prediction_without_storing_when_use_cache_false( + mock_load_pipeline, client, db_session +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-cache-false") + text = "No datapublic storage text" + + response = client.post( + f"/datapublic/predict/{document_id}", + json={"text": text}, + params={"use_cache": False}, + ) + + assert response.status_code == 200 + data = response.json() + assert "document" in data + assert "labels" in data + + paragraph_id = text_to_uuid(text) + stored = db_session.get(DataPublicParagraph, paragraph_id) + assert stored is None + + +@pytest.mark.integration +def test_should_return_422_when_document_id_not_uuid(client): + response = client.post( + "/datapublic/predict/not-a-uuid", + json={"text": "Sample text"}, + ) + + assert response.status_code == 422 + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.datapublic.datapublic.load_pipeline") +def test_should_associate_multiple_paragraphs_with_same_document( + mock_load_pipeline, client, db_session +): + mock_pipeline = build_mock_pipeline() + mock_load_pipeline.return_value = mock_pipeline + + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-associate-paragraphs") + text1 = "First paragraph for association" + text2 = "Second paragraph for association" + + response1 = client.post( + f"/datapublic/predict/{document_id}", + json={"text": text1}, + params={"use_cache": True}, + ) + + response2 = client.post( + f"/datapublic/predict/{document_id}", + json={"text": text2}, + params={"use_cache": True}, + ) + + assert response1.status_code == 200 + assert response2.status_code == 200 + + para1_id = text_to_uuid(text1) + para2_id = text_to_uuid(text2) + + stored_doc = db_session.get(DataPublicDocument, document_id) + assert stored_doc is not None + + links = ( + db_session.query(DataPublicDocumentParagraph) + .filter_by(document_id=document_id) + .all() + ) + assert len(links) == 2 + + link_para_ids = {link.paragraph_id for link in links} + assert para1_id in link_para_ids + assert para2_id in link_para_ids + + +@pytest.mark.integration +def test_should_return_404_when_validation_document_not_found(client): + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-validation-missing") + + response = client.get(f"/datapublic/validation/document/{document_id}") + + assert response.status_code == 404 + + +@pytest.mark.integration +def test_should_return_none_when_validation_not_set(client, db_session): + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-validation-empty") + db_session.add(DataPublicDocument(id=document_id)) + db_session.commit() + + response = client.get(f"/datapublic/validation/document/{document_id}") + + assert response.status_code == 200 + assert response.json() is None + + +@pytest.mark.integration +def test_should_upsert_and_read_document_validation(client, db_session): + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-validation-upsert") + payload = { + "materia": "penal", + "violencia_de_genero": "si", + "resolucion": {"tipo": "sentencia"}, + } + + post_response = client.post( + f"/datapublic/validation/document/{document_id}", + json=payload, + ) + assert post_response.status_code == 200 + + stored_doc = db_session.get(DataPublicDocument, document_id) + assert stored_doc is not None + assert stored_doc.validation == payload + + get_response = client.get(f"/datapublic/validation/document/{document_id}") + assert get_response.status_code == 200 + assert get_response.json() == payload diff --git a/tests/api/routers/misc/__init__.py b/tests/api/routers/misc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/routers/misc/test_document_extract.py b/tests/api/routers/misc/test_document_extract.py new file mode 100644 index 00000000..6a67fdd6 --- /dev/null +++ b/tests/api/routers/misc/test_document_extract.py @@ -0,0 +1,303 @@ +import concurrent.futures +import io +from unittest.mock import patch + +import pytest + +from aymurai.database.utils import data_to_uuid + + +def _build_docx_bytes(paragraphs: list[str]) -> bytes: + import docx + + document = docx.Document() + for paragraph in paragraphs: + document.add_paragraph(paragraph) + + stream = io.BytesIO() + document.save(stream) + return stream.getvalue() + + +def _build_pdf_bytes(paragraphs: list[str]) -> bytes: + import pymupdf + + pdf_document = pymupdf.open() + page = pdf_document.new_page() # type: ignore + for index, paragraph in enumerate(paragraphs): + page.insert_text((72, 72 + (index * 36)), paragraph) + + try: + to_bytes = getattr(pdf_document, "tobytes", None) + if callable(to_bytes): + serialized = to_bytes() + if isinstance(serialized, bytes): + return serialized + raise TypeError("Expected bytes from pymupdf tobytes()") + + serialized = pdf_document.write() + if isinstance(serialized, bytes): + return serialized + raise TypeError("Expected bytes from pymupdf write()") + finally: + pdf_document.close() + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_extract_real_text_from_sample_docx_without_mocking(client): + """Test that a generated DOCX is extracted without mocking.""" + expected_paragraphs = [ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.", + ] + file_content = _build_docx_bytes(expected_paragraphs) + files = { + "file": ( + "sample.docx", + file_content, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + + response = client.post("/document-extract", files=files) + + assert response.status_code == 200 + data = response.json() + assert data["document_id"] == str(data_to_uuid(file_content)) + assert data["document"] + extracted_text = " ".join(data["document"]) + for paragraph in expected_paragraphs: + assert paragraph in extracted_text + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_extract_real_text_from_pdf_without_mocking(client): + """Test that a real PDF upload is extracted without mocking.""" + expected_paragraphs = [ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.", + ] + file_content = _build_pdf_bytes(expected_paragraphs) + files = { + "file": ( + "sample.pdf", + file_content, + "application/pdf", + ) + } + + response = client.post("/document-extract", files=files) + + assert response.status_code == 200 + data = response.json() + assert data["document_id"] == str(data_to_uuid(file_content)) + assert data["document"] + extracted_text = " ".join(data["document"]) + for paragraph in expected_paragraphs: + assert paragraph in extracted_text + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_return_document_with_paragraphs_when_uploading_docx( + mock_extraction, client +): + """Test that document extraction returns paragraphs in response.""" + mock_extraction.return_value = "Para 1\nPara 2\nPara 3" + + files = { + "file": ( + "test.docx", + b"test content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/document-extract", files=files) + + assert response.status_code == 200 + data = response.json() + assert "document" in data + assert "document_id" in data + assert isinstance(data["document"], list) + assert len(data["document"]) == 3 + assert data["document"] == ["Para 1", "Para 2", "Para 3"] + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_return_document_via_misc_prefix_when_uploading(mock_extraction, client): + """Test that /misc/document-extract endpoint works.""" + mock_extraction.return_value = "Sample text" + + files = { + "file": ( + "test.docx", + b"content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/misc/document-extract", files=files) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == ["Sample text"] + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_return_document_via_deprecated_alias_when_uploading( + mock_extraction, client +): + """Test that deprecated /document-extract alias is still available.""" + mock_extraction.return_value = "Alias text" + + files = { + "file": ( + "test.docx", + b"content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/document-extract", files=files) + + assert response.status_code == 200 + assert response.json()["document"] == ["Alias text"] + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_return_deterministic_id_when_uploading_same_file_twice( + mock_extraction, client +): + """Test that same file bytes produce same document_id.""" + mock_extraction.return_value = "Same content" + + file_content = b"identical content" + files = { + "file": ( + "test.docx", + file_content, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + + response1 = client.post("/document-extract", files=files) + data1 = response1.json() + + # Reset mock and upload same file again + response2 = client.post("/document-extract", files=files) + data2 = response2.json() + + assert response1.status_code == 200 + assert response2.status_code == 200 + assert data1["document_id"] == data2["document_id"] + + +@pytest.mark.integration +def test_should_return_422_when_no_file_provided(client): + """Test that missing file returns 422 Unprocessable Entity.""" + response = client.post("/document-extract", files={}) + + assert response.status_code == 422 + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_return_empty_document_when_file_is_empty(mock_extraction, client): + """Test that empty extraction returns empty document list.""" + mock_extraction.return_value = "" + + files = { + "file": ( + "test.docx", + b"content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/document-extract", files=files) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == [] + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_collapse_consecutive_duplicates_when_extracting( + mock_extraction, client +): + """Test that consecutive duplicate paragraphs are collapsed.""" + mock_extraction.return_value = "A\nA\nB\nB\nB\nC" + + files = { + "file": ( + "test.docx", + b"content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/document-extract", files=files) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == ["A", "B", "C"] + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_normalize_whitespace_when_extracting(mock_extraction, client): + """Test that multiple spaces are normalized to single space.""" + mock_extraction.return_value = "word1 word2 word3" + + files = { + "file": ( + "test.docx", + b"content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/document-extract", files=files) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == ["word1 word2 word3"] + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_return_504_when_extraction_times_out(mock_extraction, client): + """Test that TimeoutError returns 504 Gateway Timeout.""" + mock_extraction.side_effect = concurrent.futures.TimeoutError() + + files = { + "file": ( + "test.docx", + b"content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/document-extract", files=files) + + assert response.status_code == 504 + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_return_500_when_extraction_fails(mock_extraction, client): + """Test that extraction errors return 500 Internal Server Error.""" + mock_extraction.side_effect = Exception("Extraction failed") + + files = { + "file": ( + "test.docx", + b"content", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + } + response = client.post("/document-extract", files=files) + + assert response.status_code == 500 diff --git a/tests/api/routers/test_pipeline_flows.py b/tests/api/routers/test_pipeline_flows.py new file mode 100644 index 00000000..3df22aaa --- /dev/null +++ b/tests/api/routers/test_pipeline_flows.py @@ -0,0 +1,172 @@ +import json +import shutil +import uuid +from unittest.mock import patch + +import pytest + +from aymurai.database.schema import DataPublicDocumentParagraph +from tests.api.routers.conftest import build_mock_pipeline + + +def _fake_libreoffice_convert(*args, **kwargs): + cmd = args[0] + source_path = cmd[-1] + output_path = source_path.rsplit(".", 1)[0] + ".odt" + with open(output_path, "wb") as output_file: + output_file.write(b"odt-content") + return "ok" + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.subprocess.check_output") +@patch( + "aymurai.api.endpoints.routers.anonymizer.anonymizer.map_canonical_entities_ner_preds" +) +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_canonical_dates") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.build_canonical_entities") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_run_anonymizer_flow_end_to_end( + mock_extract, + mock_load_pipeline, + mock_build_canonical_entities, + mock_get_canonical_dates, + mock_map_canonical_entities, + mock_check_output, + client, +): + mock_extract.return_value = "Ana Pérez denunció.\nJuan Soto declaró." + mock_load_pipeline.return_value = build_mock_pipeline() + mock_build_canonical_entities.return_value = [] + mock_get_canonical_dates.return_value = [] + mock_map_canonical_entities.side_effect = lambda predictions, canonical_entities: ( + predictions + ) + mock_check_output.side_effect = _fake_libreoffice_convert + + extract_response = client.post( + "/misc/document-extract", + files={ + "file": ( + "sample.docx", + b"doc-bytes", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + }, + ) + assert extract_response.status_code == 200 + paragraphs = extract_response.json()["document"] + assert len(paragraphs) == 2 + + predictions = [] + for paragraph in paragraphs: + predict_response = client.post("/anonymizer/predict", json={"text": paragraph}) + assert predict_response.status_code == 200 + predictions.append(predict_response.json()) + + disambiguate_response = client.post( + "/anonymizer/disambiguate", + json={ + "paragraphs": predictions, + "label_policies": { + "PER": {"anonymize": True, "disambiguation": "fuzzy"}, + }, + }, + ) + assert disambiguate_response.status_code == 200 + annotations = disambiguate_response.json() + assert len(annotations["data"]) == len(paragraphs) + + compile_response = client.post( + "/anonymizer/anonymize-document", + data={"annotations": json.dumps(annotations)}, + files={"file": ("sample.txt", b"doc-bytes", "text/plain")}, + ) + assert compile_response.status_code == 200 + assert compile_response.headers["content-type"] == "application/octet-stream" + + validation_response = client.post( + "/anonymizer/validation", + json={"text": paragraphs[0]}, + ) + assert validation_response.status_code == 200 + assert isinstance(validation_response.json(), list) + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.datapublic.datapublic.load_pipeline") +@patch("aymurai.api.endpoints.routers.misc.document_extract.run_safe_text_extraction") +def test_should_run_datapublic_flow_end_to_end( + mock_extract, + mock_load_pipeline, + client, + db_session, +): + mock_extract.return_value = "Primera oración.\nSegunda oración." + mock_load_pipeline.return_value = build_mock_pipeline() + + extract_response = client.post( + "/misc/document-extract", + files={ + "file": ( + "sample.docx", + b"doc-bytes", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + }, + ) + assert extract_response.status_code == 200 + paragraphs = extract_response.json()["document"] + assert len(paragraphs) == 2 + + document_id = uuid.uuid5(uuid.NAMESPACE_URL, "datapublic-e2e-flow") + for paragraph in paragraphs: + predict_response = client.post( + f"/datapublic/predict/{document_id}", + json={"text": paragraph}, + ) + assert predict_response.status_code == 200 + + links = ( + db_session.query(DataPublicDocumentParagraph) + .filter_by(document_id=document_id) + .all() + ) + assert len(links) == len(paragraphs) + + validation_payload = {"materia": "penal", "violencia_de_genero": "si"} + save_response = client.post( + f"/datapublic/validation/document/{document_id}", + json=validation_payload, + ) + assert save_response.status_code == 200 + + read_response = client.get(f"/datapublic/validation/document/{document_id}") + assert read_response.status_code == 200 + assert read_response.json() == validation_payload + + +@pytest.mark.integration +@pytest.mark.slow +@pytest.mark.external +def test_should_compile_anonymized_document_with_real_libreoffice_when_available( + client, +): + if shutil.which("libreoffice") is None: + pytest.skip("LibreOffice binary is required for real compile integration test") + + annotations = { + "data": [{"document": "Texto base para anonimizar.", "labels": []}], + "label_policies": None, + "render_policy": {"suffix_mode": "auto", "suffix_threshold": 1}, + } + + response = client.post( + "/anonymizer/anonymize-document", + data={"annotations": json.dumps(annotations)}, + files={"file": ("sample.txt", b"input-document", "text/plain")}, + ) + + assert response.status_code == 200 + assert response.headers["content-type"] == "application/octet-stream" diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/pipelines/__init__.py b/tests/integration/pipelines/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/pipelines/conftest.py b/tests/integration/pipelines/conftest.py new file mode 100644 index 00000000..548e9110 --- /dev/null +++ b/tests/integration/pipelines/conftest.py @@ -0,0 +1,63 @@ +import os +from collections.abc import Callable +from typing import TYPE_CHECKING, Any + +import pytest + +if TYPE_CHECKING: + from aymurai.pipeline.pipeline import AymurAIPipeline + +os.environ.setdefault("DISKCACHE_ROOT", "resources/cache/diskcache") +os.environ.setdefault("AYMURAI_CACHE_BASEPATH", "resources/cache/aymurai") + + +PIPELINE_CONFIGS = { + "anonymizer": "resources/pipelines/production/flair-anonymizer", + "datapublic": "resources/pipelines/production/datapublic", +} + + +def load_test_pipeline(name: str) -> "AymurAIPipeline": + from aymurai.pipeline.pipeline import AymurAIPipeline + + if name not in PIPELINE_CONFIGS: + raise ValueError( + f"Unknown pipeline: {name}. Available: {list(PIPELINE_CONFIGS.keys())}" + ) + + path = PIPELINE_CONFIGS[name] + return AymurAIPipeline.load(path, print_config=False) + + +@pytest.fixture(scope="session") +def anonymizer_pipeline() -> "AymurAIPipeline": + return load_test_pipeline("anonymizer") + + +@pytest.fixture(scope="session") +def datapublic_pipeline() -> "AymurAIPipeline": + return load_test_pipeline("datapublic") + + +@pytest.fixture +def sample_text() -> str: + return ( + "El día 15 de marzo de 2023, el Sr. Juan Pérez fue imputado por el delito de " + "lesiones graves. La víctima, María González, declaró en el Juzgado Civil de Buenos Aires." + ) + + +def _build_pipeline_input(text: str) -> dict[str, Any]: + return { + "path": "test", + "extension": "", + "dataset": "", + "data": {"doc.text": text}, + "annotations": None, + "predictions": {}, + } + + +@pytest.fixture +def build_pipeline_input() -> Callable[[str], dict[str, Any]]: + return _build_pipeline_input diff --git a/tests/integration/pipelines/test_anonymizer.py b/tests/integration/pipelines/test_anonymizer.py new file mode 100644 index 00000000..faf461a2 --- /dev/null +++ b/tests/integration/pipelines/test_anonymizer.py @@ -0,0 +1,95 @@ +from typing import Any + +import pytest + +from aymurai.pipeline.pipeline import AymurAIPipeline + + +def _extract_entities(item: dict[str, Any]) -> list[dict[str, Any]]: + predictions = item.get("predictions") + if isinstance(predictions, dict): + entities = predictions.get("entities") or [] + if isinstance(entities, list): + return [entity for entity in entities if isinstance(entity, dict)] + return [] + + +@pytest.fixture +def input_item(sample_text: str, build_pipeline_input) -> dict[str, Any]: + return build_pipeline_input(sample_text) + + +@pytest.fixture +def preprocessed_item( + anonymizer_pipeline, + input_item: dict[str, Any], +) -> dict[str, Any]: + return anonymizer_pipeline.preprocess([input_item])[0] + + +@pytest.fixture +def predicted_item( + anonymizer_pipeline, + preprocessed_item: dict[str, Any], +) -> dict[str, Any]: + return anonymizer_pipeline.predict_single(preprocessed_item) + + +@pytest.fixture +def postprocessed_item( + anonymizer_pipeline, + predicted_item: dict[str, Any], +) -> dict[str, Any]: + return anonymizer_pipeline.postprocess([predicted_item])[0] + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_load_pipeline_when_given_production_config(anonymizer_pipeline): + assert isinstance(anonymizer_pipeline, AymurAIPipeline) + assert hasattr(anonymizer_pipeline, "pre_process") + assert hasattr(anonymizer_pipeline, "training_pipeline") + assert hasattr(anonymizer_pipeline, "post_process") + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_preprocess_when_given_text_input(preprocessed_item: dict[str, Any]): + assert isinstance(preprocessed_item, dict) + assert "data" in preprocessed_item + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_predict_single_when_given_preprocessed_item( + predicted_item: dict[str, Any], +): + assert isinstance(predicted_item, dict) + assert "predictions" in predicted_item + assert predicted_item["predictions"] is not None + entities = _extract_entities(predicted_item) + assert entities, "Expected at least one anonymizer entity in canonical sample text" + first_entity = entities[0] + assert first_entity.get("text") + assert isinstance(first_entity.get("start_char"), int) + assert isinstance(first_entity.get("end_char"), int) + assert isinstance(first_entity.get("attrs"), dict) + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_postprocess_when_given_predicted_items( + postprocessed_item: dict[str, Any], +): + assert isinstance(postprocessed_item, dict) + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_produce_anonymization_entities_when_running_full_chain( + postprocessed_item: dict[str, Any], +): + result = postprocessed_item + assert "predictions" in result + entities = _extract_entities(result) + assert entities, "Expected postprocess to preserve anonymizer entities" diff --git a/tests/integration/pipelines/test_datapublic.py b/tests/integration/pipelines/test_datapublic.py new file mode 100644 index 00000000..23a0c58b --- /dev/null +++ b/tests/integration/pipelines/test_datapublic.py @@ -0,0 +1,95 @@ +from typing import Any + +import pytest + +from aymurai.pipeline.pipeline import AymurAIPipeline + + +def _extract_entities(item: dict[str, Any]) -> list[dict[str, Any]]: + predictions = item.get("predictions") + if isinstance(predictions, dict): + entities = predictions.get("entities") or [] + if isinstance(entities, list): + return [entity for entity in entities if isinstance(entity, dict)] + return [] + + +@pytest.fixture +def input_item(sample_text: str, build_pipeline_input) -> dict[str, Any]: + return build_pipeline_input(sample_text) + + +@pytest.fixture +def preprocessed_item( + datapublic_pipeline, + input_item: dict[str, Any], +) -> dict[str, Any]: + return datapublic_pipeline.preprocess([input_item])[0] + + +@pytest.fixture +def predicted_item( + datapublic_pipeline, + preprocessed_item: dict[str, Any], +) -> dict[str, Any]: + return datapublic_pipeline.predict_single(preprocessed_item) + + +@pytest.fixture +def postprocessed_item( + datapublic_pipeline, + predicted_item: dict[str, Any], +) -> dict[str, Any]: + return datapublic_pipeline.postprocess([predicted_item])[0] + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_load_pipeline_when_given_production_config(datapublic_pipeline): + assert isinstance(datapublic_pipeline, AymurAIPipeline) + assert hasattr(datapublic_pipeline, "pre_process") + assert hasattr(datapublic_pipeline, "training_pipeline") + assert hasattr(datapublic_pipeline, "post_process") + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_preprocess_when_given_text_input(preprocessed_item: dict[str, Any]): + assert isinstance(preprocessed_item, dict) + assert "data" in preprocessed_item + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_predict_single_when_given_preprocessed_item( + predicted_item: dict[str, Any], +): + assert isinstance(predicted_item, dict) + assert "predictions" in predicted_item + assert predicted_item["predictions"] is not None + entities = _extract_entities(predicted_item) + assert entities, "Expected at least one datapublic entity in canonical sample text" + first_entity = entities[0] + assert first_entity.get("text") + assert isinstance(first_entity.get("start_char"), int) + assert isinstance(first_entity.get("end_char"), int) + assert isinstance(first_entity.get("attrs"), dict) + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_postprocess_when_given_predicted_items( + postprocessed_item: dict[str, Any], +): + assert isinstance(postprocessed_item, dict) + + +@pytest.mark.integration +@pytest.mark.slow +def test_should_produce_entities_when_running_full_chain( + postprocessed_item: dict[str, Any], +): + result = postprocessed_item + assert "predictions" in result + entities = _extract_entities(result) + assert entities, "Expected postprocess to preserve datapublic entities" diff --git a/uv.lock b/uv.lock index 738af2d2..2d4cd1e6 100644 --- a/uv.lock +++ b/uv.lock @@ -1,9 +1,14 @@ version = 1 +revision = 3 requires-python = "==3.10.*" +required-markers = [ + "platform_machine == 'x86_64' and sys_platform == 'linux'", + "platform_machine == 'AMD64' and sys_platform == 'win32'", +] [[package]] name = "accelerate" -version = "1.12.0" +version = "1.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -14,18 +19,18 @@ dependencies = [ { name = "safetensors" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/8e/ac2a9566747a93f8be36ee08532eb0160558b07630a081a6056a9f89bf1d/accelerate-1.12.0.tar.gz", hash = "sha256:70988c352feb481887077d2ab845125024b2a137a5090d6d7a32b57d03a45df6", size = 398399 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl", hash = "sha256:3e2091cd341423207e2f084a6654b1efcd250dc326f2a37d6dde446e07cabb11", size = 380935 }, + { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, ] [[package]] name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] [[package]] @@ -42,25 +47,25 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556 } +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950 }, - { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099 }, - { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072 }, - { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588 }, - { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334 }, - { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656 }, - { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625 }, - { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604 }, - { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370 }, - { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023 }, - { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680 }, - { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407 }, - { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047 }, - { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264 }, - { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275 }, - { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053 }, - { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687 }, + { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, + { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, + { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, + { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, + { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, + { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, + { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, + { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, + { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, ] [[package]] @@ -71,9 +76,9 @@ dependencies = [ { name = "frozenlist" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] [[package]] @@ -86,27 +91,27 @@ dependencies = [ { name = "tomli" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725 } +sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893 }, + { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" }, ] [[package]] name = "annotated-doc" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] @@ -118,18 +123,18 @@ dependencies = [ { name = "idna" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] [[package]] name = "appnope" version = "0.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, ] [[package]] @@ -139,9 +144,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, ] [[package]] @@ -151,23 +156,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, - { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549 }, - { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539 }, - { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467 }, - { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355 }, - { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, + { url = "https://files.pythonhosted.org/packages/11/2d/ba4e4ca8d149f8dcc0d952ac0967089e1d759c7e5fcf0865a317eb680fbb/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e", size = 24549, upload-time = "2025-07-30T10:02:00.101Z" }, + { url = "https://files.pythonhosted.org/packages/5c/82/9b2386cc75ac0bd3210e12a44bfc7fd1632065ed8b80d573036eecb10442/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d", size = 25539, upload-time = "2025-07-30T10:02:00.929Z" }, + { url = "https://files.pythonhosted.org/packages/31/db/740de99a37aa727623730c90d92c22c9e12585b3c98c54b7960f7810289f/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584", size = 28467, upload-time = "2025-07-30T10:02:02.08Z" }, + { url = "https://files.pythonhosted.org/packages/71/7a/47c4509ea18d755f44e2b92b7178914f0c113946d11e16e626df8eaa2b0b/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690", size = 27355, upload-time = "2025-07-30T10:02:02.867Z" }, + { url = "https://files.pythonhosted.org/packages/ee/82/82745642d3c46e7cea25e1885b014b033f4693346ce46b7f47483cf5d448/argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520", size = 29187, upload-time = "2025-07-30T10:02:03.674Z" }, ] [[package]] @@ -178,18 +183,18 @@ dependencies = [ { name = "python-dateutil" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, ] [[package]] name = "asttokens" version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] [[package]] @@ -199,32 +204,31 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/8a/ca724066c32a53fa75f59e0f21aa822fdaa8a0dffa112d223634e3caabf9/async_lru-2.2.0.tar.gz", hash = "sha256:80abae2a237dbc6c60861d621619af39f0d920aea306de34cb992c879e01370c", size = 14654 } +sdist = { url = "https://files.pythonhosted.org/packages/05/8a/ca724066c32a53fa75f59e0f21aa822fdaa8a0dffa112d223634e3caabf9/async_lru-2.2.0.tar.gz", hash = "sha256:80abae2a237dbc6c60861d621619af39f0d920aea306de34cb992c879e01370c", size = 14654, upload-time = "2026-02-20T19:11:43.848Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl", hash = "sha256:e2c1cf731eba202b59c5feedaef14ffd9d02ad0037fcda64938699f2c380eafe", size = 7890 }, + { url = "https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl", hash = "sha256:e2c1cf731eba202b59c5feedaef14ffd9d02ad0037fcda64938699f2c380eafe", size = 7890, upload-time = "2026-02-20T19:11:42.273Z" }, ] [[package]] name = "async-timeout" version = "5.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, ] [[package]] name = "attrs" version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] [[package]] name = "aymurai" -version = "1.5.0rc1" source = { editable = "." } dependencies = [ { name = "alembic" }, @@ -250,7 +254,6 @@ dependencies = [ { name = "python-docx" }, { name = "python-dotenv" }, { name = "python-multipart" }, - { name = "pytorch-lightning" }, { name = "requests" }, { name = "scipy" }, { name = "sentence-transformers" }, @@ -274,6 +277,9 @@ dev = [ { name = "rich" }, { name = "seaborn" }, ] +tests = [ + { name = "pytest" }, +] [package.metadata] requires-dist = [ @@ -300,14 +306,13 @@ requires-dist = [ { name = "python-docx", specifier = ">=1.2.0" }, { name = "python-dotenv", specifier = ">=1.0.1" }, { name = "python-multipart", specifier = ">=0.0.20" }, - { name = "pytorch-lightning", specifier = "==1.8.3.post1" }, { name = "requests", specifier = ">=2.32.3" }, { name = "scipy", specifier = "<1.14.1" }, { name = "sentence-transformers", specifier = ">=2.2.0" }, { name = "sentencepiece", specifier = "==0.2.0" }, { name = "sqlmodel", specifier = "==0.0.22" }, { name = "tenacity", specifier = ">=9.0.0" }, - { name = "torch", specifier = ">=1.13.1" }, + { name = "torch", specifier = ">=2.0" }, { name = "unidecode", specifier = "==1.3.8" }, { name = "uvicorn", specifier = ">=0.34.0" }, { name = "xmltodict", specifier = "==0.14.2" }, @@ -324,14 +329,15 @@ dev = [ { name = "rich", specifier = ">=13.9.4" }, { name = "seaborn", specifier = ">=0.13.2" }, ] +tests = [{ name = "pytest", specifier = ">=9.0.2" }] [[package]] name = "babel" version = "2.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] [[package]] @@ -342,9 +348,9 @@ dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] [[package]] @@ -358,9 +364,9 @@ dependencies = [ { name = "lxml" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924 } +sdist = { url = "https://files.pythonhosted.org/packages/44/9b/90086bbbffcf4aaa267a71e94f92eab1cfe4142198708129b8495e16c73f/bioc-2.1.tar.gz", hash = "sha256:7d9248198bdae291b0ebb218de2dc653c96d32a7eac2fa0ef4ed0c74ce45aaaa", size = 27924, upload-time = "2023-08-15T22:34:59.047Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419 }, + { url = "https://files.pythonhosted.org/packages/4b/05/ae6dcab59673e2d498c645903ded6c5d76f7d44920386285feb96c517b3a/bioc-2.1-py3-none-any.whl", hash = "sha256:f1730d26821330f9f625058841612d6cfb616efb906fc682297fe04dd5d9398a", size = 33419, upload-time = "2023-08-15T22:34:56.022Z" }, ] [[package]] @@ -370,9 +376,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533, upload-time = "2025-10-27T17:57:39.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437, upload-time = "2025-10-27T17:57:37.538Z" }, ] [package.optional-dependencies] @@ -382,48 +388,48 @@ css = [ [[package]] name = "boto3" -version = "1.42.54" +version = "1.42.66" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/53/2e0a325e080bd83f5dfd8f964b70b93badc284bcb5680bee75327771ad4a/boto3-1.42.54.tar.gz", hash = "sha256:fe3d8ec586c39a0c96327fd317c77ca601ec5f991e9ba7211cacae8db4c07a73", size = 112747 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/2e/67206daa5acb6053157ae5241421713a84ed6015d33d0781985bd5558898/boto3-1.42.66.tar.gz", hash = "sha256:3bec5300fb2429c3be8e8961fdb1f11e85195922c8a980022332c20af05616d5", size = 112805, upload-time = "2026-03-11T19:58:19.17Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d6/695283df0a613cb723a05745cd565061add2bc5655d3493341b8b5c6b81d/boto3-1.42.54-py3-none-any.whl", hash = "sha256:71194e855bfc81a21872cbe29c41f52ffdbe67e0a184a52c13346ef00b328939", size = 140555 }, + { url = "https://files.pythonhosted.org/packages/4c/09/83224363c3f5e468e298e48beb577ffe8cb51f18c2116bc1ecf404796e60/boto3-1.42.66-py3-none-any.whl", hash = "sha256:7c6c60dc5500e8a2967a306372a5fdb4c7f9a5b8adc5eb9aa2ebb5081c51ff47", size = 140557, upload-time = "2026-03-11T19:58:17.61Z" }, ] [[package]] name = "botocore" -version = "1.42.54" +version = "1.42.66" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/9a/5ab14330e5d1c3489e91f32f6ece40f3b58cf82d2aafe1e4a61711f616b0/botocore-1.42.54.tar.gz", hash = "sha256:ab203d4e57d22913c8386a695d048e003b7508a8a4a7a46c9ddf4ebd67a20b69", size = 14921929 } +sdist = { url = "https://files.pythonhosted.org/packages/77/ef/1c8f89da69b0c3742120e19a6ea72ec46ac0596294466924fdd4cf0f36bb/botocore-1.42.66.tar.gz", hash = "sha256:39756a21142b646de552d798dde2105759b0b8fa0d881a34c26d15bd4c9448fa", size = 14977446, upload-time = "2026-03-11T19:58:07.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/29/cdf4ba5d0f626b7c5a74d6a615b977469960eae8c67f8e4213941f5f3dfd/botocore-1.42.54-py3-none-any.whl", hash = "sha256:853a0822de66d060aeebafa07ca13a03799f7958313d1b29f8dc7e2e1be8f527", size = 14594249 }, + { url = "https://files.pythonhosted.org/packages/13/6f/7b45ed2ca300c1ad38ecfc82c1368546d4a90512d9dff589ebbd182a7317/botocore-1.42.66-py3-none-any.whl", hash = "sha256:ac48af1ab527dfa08c4617c387413ca56a7f87780d7bfc1da34ef847a59219a5", size = 14653886, upload-time = "2026-03-11T19:58:04.922Z" }, ] [[package]] name = "cachetools" -version = "7.0.1" +version = "7.0.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/07/56595285564e90777d758ebd383d6b0b971b87729bbe2184a849932a3736/cachetools-7.0.1.tar.gz", hash = "sha256:e31e579d2c5b6e2944177a0397150d312888ddf4e16e12f1016068f0c03b8341", size = 36126 } +sdist = { url = "https://files.pythonhosted.org/packages/af/dd/57fe3fdb6e65b25a5987fd2cdc7e22db0aef508b91634d2e57d22928d41b/cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990", size = 37367, upload-time = "2026-03-09T20:51:29.451Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/9e/5faefbf9db1db466d633735faceda1f94aa99ce506ac450d232536266b32/cachetools-7.0.1-py3-none-any.whl", hash = "sha256:8f086515c254d5664ae2146d14fc7f65c9a4bce75152eb247e5a9c5e6d7b2ecf", size = 13484 }, + { url = "https://files.pythonhosted.org/packages/06/f3/39cf3367b8107baa44f861dc802cbf16263c945b62d8265d36034fc07bea/cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114", size = 13918, upload-time = "2026-03-09T20:51:27.33Z" }, ] [[package]] name = "certifi" -version = "2026.1.4" +version = "2026.2.25" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268 } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900 }, + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, ] [[package]] @@ -433,54 +439,54 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283 }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504 }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811 }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402 }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217 }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079 }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475 }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829 }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211 }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036 }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184 }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790 }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, ] [[package]] name = "cfgv" version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] [[package]] name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 }, - { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 }, - { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 }, - { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 }, - { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 }, - { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 }, - { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 }, - { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 }, - { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 }, - { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 }, - { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 }, - { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 }, - { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 }, - { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 }, - { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 }, - { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/21/a2b1505639008ba2e6ef03733a81fc6cfd6a07ea6139a2b76421230b8dad/charset_normalizer-3.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4167a621a9a1a986c73777dbc15d4b5eac8ac5c10393374109a343d4013ec765", size = 283319, upload-time = "2026-03-06T06:00:26.433Z" }, + { url = "https://files.pythonhosted.org/packages/70/67/df234c29b68f4e1e095885c9db1cb4b69b8aba49cf94fac041db4aaf1267/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f64c6bf8f32f9133b668c7f7a7cbdbc453412bc95ecdbd157f3b1e377a92990", size = 189974, upload-time = "2026-03-06T06:00:28.222Z" }, + { url = "https://files.pythonhosted.org/packages/df/7f/fc66af802961c6be42e2c7b69c58f95cbd1f39b0e81b3365d8efe2a02a04/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:568e3c34b58422075a1b49575a6abc616d9751b4d61b23f712e12ebb78fe47b2", size = 207866, upload-time = "2026-03-06T06:00:29.769Z" }, + { url = "https://files.pythonhosted.org/packages/c9/23/404eb36fac4e95b833c50e305bba9a241086d427bb2167a42eac7c4f7da4/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:036c079aa08a6a592b82487f97c60b439428320ed1b2ea0b3912e99d30c77765", size = 203239, upload-time = "2026-03-06T06:00:31.086Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2f/8a1d989bfadd120c90114ab33e0d2a0cbde05278c1fc15e83e62d570f50a/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340810d34ef83af92148e96e3e44cb2d3f910d2bf95e5618a5c467d9f102231d", size = 196529, upload-time = "2026-03-06T06:00:32.608Z" }, + { url = "https://files.pythonhosted.org/packages/a5/0c/c75f85ff7ca1f051958bb518cd43922d86f576c03947a050fbedfdfb4f15/charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:cd2d0f0ec9aa977a27731a3209ebbcacebebaf41f902bd453a928bfd281cf7f8", size = 184152, upload-time = "2026-03-06T06:00:33.93Z" }, + { url = "https://files.pythonhosted.org/packages/f9/20/4ed37f6199af5dde94d4aeaf577f3813a5ec6635834cda1d957013a09c76/charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b362bcd27819f9c07cbf23db4e0e8cd4b44c5ecd900c2ff907b2b92274a7412", size = 195226, upload-time = "2026-03-06T06:00:35.469Z" }, + { url = "https://files.pythonhosted.org/packages/28/31/7ba1102178cba7c34dcc050f43d427172f389729e356038f0726253dd914/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:77be992288f720306ab4108fe5c74797de327f3248368dfc7e1a916d6ed9e5a2", size = 192933, upload-time = "2026-03-06T06:00:36.83Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/f86443ab3921e6a60b33b93f4a1161222231f6c69bc24fb18f3bee7b8518/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8b78d8a609a4b82c273257ee9d631ded7fac0d875bdcdccc109f3ee8328cfcb1", size = 185647, upload-time = "2026-03-06T06:00:38.367Z" }, + { url = "https://files.pythonhosted.org/packages/82/44/08b8be891760f1f5a6d23ce11d6d50c92981603e6eb740b4f72eea9424e2/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ba20bdf69bd127f66d0174d6f2a93e69045e0b4036dc1ca78e091bcc765830c4", size = 209533, upload-time = "2026-03-06T06:00:41.931Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5f/df114f23406199f8af711ddccfbf409ffbc5b7cdc18fa19644997ff0c9bb/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:76a9d0de4d0eab387822e7b35d8f89367dd237c72e82ab42b9f7bf5e15ada00f", size = 195901, upload-time = "2026-03-06T06:00:43.978Z" }, + { url = "https://files.pythonhosted.org/packages/07/83/71ef34a76fe8aa05ff8f840244bda2d61e043c2ef6f30d200450b9f6a1be/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8fff79bf5978c693c9b1a4d71e4a94fddfb5fe744eb062a318e15f4a2f63a550", size = 204950, upload-time = "2026-03-06T06:00:45.202Z" }, + { url = "https://files.pythonhosted.org/packages/58/40/0253be623995365137d7dc68e45245036207ab2227251e69a3d93ce43183/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c7e84e0c0005e3bdc1a9211cd4e62c78ba80bc37b2365ef4410cd2007a9047f2", size = 198546, upload-time = "2026-03-06T06:00:46.481Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5c/5f3cb5b259a130895ef5ae16b38eaf141430fa3f7af50cd06c5d67e4f7b2/charset_normalizer-3.4.5-cp310-cp310-win32.whl", hash = "sha256:58ad8270cfa5d4bef1bc85bd387217e14ff154d6630e976c6f56f9a040757475", size = 132516, upload-time = "2026-03-06T06:00:47.924Z" }, + { url = "https://files.pythonhosted.org/packages/a5/c3/84fb174e7770f2df2e1a2115090771bfbc2227fb39a765c6d00568d1aab4/charset_normalizer-3.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:02a9d1b01c1e12c27883b0c9349e0bcd9ae92e727ff1a277207e1a262b1cbf05", size = 142906, upload-time = "2026-03-06T06:00:49.389Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b2/6f852f8b969f2cbd0d4092d2e60139ab1af95af9bb651337cae89ec0f684/charset_normalizer-3.4.5-cp310-cp310-win_arm64.whl", hash = "sha256:039215608ac7b358c4da0191d10fc76868567fbf276d54c14721bdedeb6de064", size = 133258, upload-time = "2026-03-06T06:00:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" }, ] [[package]] @@ -488,38 +494,50 @@ name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coloredlogs" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "humanfriendly" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, ] [[package]] name = "comm" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, ] [[package]] name = "conllu" version = "4.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768 } +sdist = { url = "https://files.pythonhosted.org/packages/7b/48/3e539bb27777d2204381e6bd352225d02965acce6e5a8d0717e9750dcc77/conllu-4.5.3.tar.gz", hash = "sha256:a016cf77e203b2e3ace82fcf0cba2874530d1458e874521640eba36e19546acc", size = 32768, upload-time = "2023-06-19T12:37:49.632Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098 }, + { url = "https://files.pythonhosted.org/packages/ce/3f/70a1dc5bc536755ec082b806594598a10cfffaf0de978f51d4e0e4fdfa47/conllu-4.5.3-py2.py3-none-any.whl", hash = "sha256:2b962b315c3c575e429105d045c888726df780b87a6dfe7609367e861990902d", size = 16098, upload-time = "2023-06-19T12:37:47.885Z" }, ] [[package]] @@ -529,21 +547,21 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551 }, - { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399 }, - { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061 }, - { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956 }, - { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872 }, - { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027 }, - { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641 }, - { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075 }, - { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534 }, - { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188 }, - { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681 }, - { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101 }, - { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599 }, + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, ] [[package]] @@ -554,30 +572,29 @@ dependencies = [ { name = "cuda-pathfinder" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/37/31/bfcc870f69c6a017c4ad5c42316207fc7551940db6f3639aa4466ec5faf3/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a022c96b8bd847e8dc0675523431149a4c3e872f440e3002213dbb9e08f0331a", size = 11800959 }, - { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218 }, + { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218, upload-time = "2025-10-21T14:51:28.855Z" }, ] [[package]] name = "cuda-pathfinder" -version = "1.3.4" +version = "1.4.2" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/5e/db279a3bfbd18d59d0598922a3b3c1454908d0969e8372260afec9736376/cuda_pathfinder-1.3.4-py3-none-any.whl", hash = "sha256:fb983f6e0d43af27ef486e14d5989b5f904ef45cedf40538bfdcbffa6bb01fb2", size = 30878 }, + { url = "https://files.pythonhosted.org/packages/92/de/8ca2b613042550dcf9ef50c596c8b1f602afda92cf9032ac28a73f6ee410/cuda_pathfinder-1.4.2-py3-none-any.whl", hash = "sha256:eb354abc20278f8609dc5b666a24648655bef5613c6dfe78a238a6fd95566754", size = 44779, upload-time = "2026-03-10T21:57:30.974Z" }, ] [[package]] name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] [[package]] name = "datasets" -version = "4.5.0" +version = "4.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -595,9 +612,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/bf/bb927bde63d649296c83e883171ae77074717c1b80fe2868b328bd0dbcbb/datasets-4.5.0.tar.gz", hash = "sha256:00c698ce1c2452e646cc5fad47fef39d3fe78dd650a8a6eb205bb45eb63cd500", size = 588384 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/9c/ba18de0b70858533e422ed6cfe0e46789473cef7fc7fc3653e23fa494730/datasets-4.7.0.tar.gz", hash = "sha256:4984cdfc65d04464da7f95205a55cb50515fd94ae3176caacb50a1b7273792e2", size = 602008, upload-time = "2026-03-09T19:01:49.298Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/d5/0d563ea3c205eee226dc8053cf7682a8ac588db8acecd0eda2b587987a0b/datasets-4.5.0-py3-none-any.whl", hash = "sha256:b5d7e08096ffa407dd69e58b1c0271c9b2506140839b8d99af07375ad31b6726", size = 515196 }, + { url = "https://files.pythonhosted.org/packages/1e/03/c6d9c3119cf712f638fe763e887ecaac6acbb62bf1e2acc3cbde0df340fd/datasets-4.7.0-py3-none-any.whl", hash = "sha256:d5fe3025ec6acc3b5649f10d5576dff5e054134927604e6913c1467a04adc3c2", size = 527530, upload-time = "2026-03-09T19:01:47.443Z" }, ] [[package]] @@ -609,31 +626,31 @@ source = { git = "https://github.com/jedzill4/datetime_matcher#0e5793e8d1e3653f7 name = "debugpy" version = "1.8.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207, upload-time = "2026-01-29T23:03:28.199Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/be/8bd693a0b9d53d48c8978fa5d889e06f3b5b03e45fd1ea1e78267b4887cb/debugpy-1.8.20-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:157e96ffb7f80b3ad36d808646198c90acb46fdcfd8bb1999838f0b6f2b59c64", size = 2099192 }, - { url = "https://files.pythonhosted.org/packages/77/1b/85326d07432086a06361d493d2743edd0c4fc2ef62162be7f8618441ac37/debugpy-1.8.20-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:c1178ae571aff42e61801a38b007af504ec8e05fde1c5c12e5a7efef21009642", size = 3088568 }, - { url = "https://files.pythonhosted.org/packages/e8/60/3e08462ee3eccd10998853eb35947c416e446bfe2bc37dbb886b9044586c/debugpy-1.8.20-cp310-cp310-win32.whl", hash = "sha256:c29dd9d656c0fbd77906a6e6a82ae4881514aa3294b94c903ff99303e789b4a2", size = 5284399 }, - { url = "https://files.pythonhosted.org/packages/72/43/09d49106e770fe558ced5e80df2e3c2ebee10e576eda155dcc5670473663/debugpy-1.8.20-cp310-cp310-win_amd64.whl", hash = "sha256:3ca85463f63b5dd0aa7aaa933d97cbc47c174896dcae8431695872969f981893", size = 5316388 }, - { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658 }, + { url = "https://files.pythonhosted.org/packages/71/be/8bd693a0b9d53d48c8978fa5d889e06f3b5b03e45fd1ea1e78267b4887cb/debugpy-1.8.20-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:157e96ffb7f80b3ad36d808646198c90acb46fdcfd8bb1999838f0b6f2b59c64", size = 2099192, upload-time = "2026-01-29T23:03:29.707Z" }, + { url = "https://files.pythonhosted.org/packages/77/1b/85326d07432086a06361d493d2743edd0c4fc2ef62162be7f8618441ac37/debugpy-1.8.20-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:c1178ae571aff42e61801a38b007af504ec8e05fde1c5c12e5a7efef21009642", size = 3088568, upload-time = "2026-01-29T23:03:31.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/60/3e08462ee3eccd10998853eb35947c416e446bfe2bc37dbb886b9044586c/debugpy-1.8.20-cp310-cp310-win32.whl", hash = "sha256:c29dd9d656c0fbd77906a6e6a82ae4881514aa3294b94c903ff99303e789b4a2", size = 5284399, upload-time = "2026-01-29T23:03:33.678Z" }, + { url = "https://files.pythonhosted.org/packages/72/43/09d49106e770fe558ced5e80df2e3c2ebee10e576eda155dcc5670473663/debugpy-1.8.20-cp310-cp310-win_amd64.whl", hash = "sha256:3ca85463f63b5dd0aa7aaa933d97cbc47c174896dcae8431695872969f981893", size = 5316388, upload-time = "2026-01-29T23:03:35.095Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658, upload-time = "2026-01-29T23:04:17.404Z" }, ] [[package]] name = "decorator" version = "5.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] [[package]] name = "defusedxml" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, ] [[package]] @@ -643,60 +660,60 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] [[package]] name = "dill" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976 } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668 }, + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, ] [[package]] name = "diskcache" version = "5.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] [[package]] name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] [[package]] name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } [[package]] name = "docx2txt" version = "0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/07/4486a038624e885e227fe79111914c01f55aa70a51920ff1a7f2bd216d10/docx2txt-0.9.tar.gz", hash = "sha256:18013f6229b14909028b19aa7bf4f8f3d6e4632d7b089ab29f7f0a4d1f660e28", size = 3613, upload-time = "2025-03-24T20:59:25.21Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025 }, + { url = "https://files.pythonhosted.org/packages/d6/51/756e71bec48ece0ecc2a10e921ef2756e197dcb7e478f2b43673b6683902/docx2txt-0.9-py3-none-any.whl", hash = "sha256:e3718c0653fd6f2fcf4b51b02a61452ad1c38a4c163bcf0a6fd9486cd38f529a", size = 4025, upload-time = "2025-03-24T20:59:24.394Z" }, ] [[package]] @@ -707,9 +724,9 @@ dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] [[package]] @@ -719,18 +736,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] [[package]] name = "executing" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] [[package]] @@ -740,14 +757,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/97/d7f0b25e181738b24e25092acc160bf30c682da6ab961e28f839a5f862b2/Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d", size = 1670644, upload-time = "2023-06-27T15:24:28.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039 }, + { url = "https://files.pythonhosted.org/packages/db/d5/cbb9d7083e1ba9ace3434f12c8bccb58e9ec5127323c09efea5d5fc59b1d/Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955", size = 1710039, upload-time = "2023-06-27T15:24:24.869Z" }, ] [[package]] name = "fastapi" -version = "0.132.0" +version = "0.135.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -756,9 +773,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a0/55/f1b4d4e478a0a1b4b1113d0f610a1b08e539b69900f97fdc97155d62fdee/fastapi-0.132.0.tar.gz", hash = "sha256:ef687847936d8a57ea6ea04cf9a85fe5f2c6ba64e22bfa721467094b69d48d92", size = 372422 } +sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/de/6171c3363bbc5e01686e200e0880647c9270daa476d91030435cf14d32f5/fastapi-0.132.0-py3-none-any.whl", hash = "sha256:3c487d5afce196fa8ea509ae1531e96ccd5cdd2fd6eae78b73e2c20fba706689", size = 104652 }, + { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, ] [package.optional-dependencies] @@ -775,7 +792,7 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.23" +version = "0.0.24" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, @@ -783,9 +800,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/9f/cbd463e57de4e977b8ea0403f95347f9150441568b1d3fe3e4949ef80ef3/fastapi_cli-0.0.23.tar.gz", hash = "sha256:210ac280ea41e73aac5a57688781256beb23c2cba3a41266896fa43e6445c8e7", size = 19763 } +sdist = { url = "https://files.pythonhosted.org/packages/6e/58/74797ae9e4610cfa0c6b34c8309096d3b20bb29be3b8b5fbf1004d10fa5f/fastapi_cli-0.0.24.tar.gz", hash = "sha256:1afc9c9e21d7ebc8a3ca5e31790cd8d837742be7e4f8b9236e99cb3451f0de00", size = 19043, upload-time = "2026-02-24T10:45:10.476Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/89/19dcfd5cd289b306abdcabac68b88a4f54b7710a2c33adc16a337ecdcdfa/fastapi_cli-0.0.23-py3-none-any.whl", hash = "sha256:7e9634fc212da0b6cfc75bd3ac366cc9dfdb43b5e9ec12e58bfd1acdd2697f25", size = 12305 }, + { url = "https://files.pythonhosted.org/packages/c7/4b/68f9fe268e535d79c76910519530026a4f994ce07189ac0dded45c6af825/fastapi_cli-0.0.24-py3-none-any.whl", hash = "sha256:4a1f78ed798f106b4fee85ca93b85d8fe33c0a3570f775964d37edb80b8f0edc", size = 12304, upload-time = "2026-02-24T10:45:09.552Z" }, ] [package.optional-dependencies] @@ -796,7 +813,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.13.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastar" }, @@ -808,73 +825,61 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/0b/f07f4976784978ef159fd2e8f5c16f1f9d610578fb1fd976ff1315c11ea6/fastapi_cloud_cli-0.13.0.tar.gz", hash = "sha256:4d8f42337e8021c648f6cb0672de7d5b31b0fc7387a83d7b12f974600ac3f2fd", size = 38436 } +sdist = { url = "https://files.pythonhosted.org/packages/63/e1/05c44e7bbc619e980fab0236cff9f5f323ac1aaa79434b4906febf98b1d3/fastapi_cloud_cli-0.15.0.tar.gz", hash = "sha256:d02515231f3f505f7669c20920343934570a88a08af9f9a6463ca2807f27ffe5", size = 45309, upload-time = "2026-03-11T22:31:32.455Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/88/71a1e989d17b9edb483f32e28b7891ffdd3005271518c98ba6415987c430/fastapi_cloud_cli-0.13.0-py3-none-any.whl", hash = "sha256:874a9ed8dba34ec828f198c72de9f9a38de77ac1b15083d6bc3a4d772b0bc477", size = 27631 }, + { url = "https://files.pythonhosted.org/packages/40/cc/1ccca747f5609be27186ea8c9219449142f40e3eded2c6089bba6a6ecc82/fastapi_cloud_cli-0.15.0-py3-none-any.whl", hash = "sha256:9ffcf90bd713747efa65447620d29cfbb7b3f7de38d97467952ca6346e418d70", size = 32267, upload-time = "2026-03-11T22:31:33.499Z" }, ] [[package]] name = "fastar" version = "0.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536 }, - { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235 }, - { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386 }, - { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955 }, - { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987 }, - { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900 }, - { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523 }, - { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268 }, - { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286 }, - { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921 }, - { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302 }, - { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141 }, - { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544 }, - { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701 }, - { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544 }, - { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020 }, - { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735 }, - { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779 }, - { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755 }, - { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732 }, - { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571 }, - { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440 }, - { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424 }, - { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675 }, - { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098 }, - { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397 }, +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536, upload-time = "2025-11-26T02:34:35.236Z" }, + { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235, upload-time = "2025-11-26T02:34:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386, upload-time = "2025-11-26T02:33:47.613Z" }, + { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955, upload-time = "2025-11-26T02:32:44.279Z" }, + { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987, upload-time = "2025-11-26T02:32:59.701Z" }, + { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900, upload-time = "2025-11-26T02:33:16.059Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523, upload-time = "2025-11-26T02:33:30.897Z" }, + { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268, upload-time = "2025-11-26T02:34:04.003Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286, upload-time = "2025-11-26T02:34:50.279Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921, upload-time = "2025-11-26T02:35:07.292Z" }, + { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302, upload-time = "2025-11-26T02:35:24.995Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141, upload-time = "2025-11-26T02:35:42.449Z" }, + { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544, upload-time = "2025-11-26T02:36:23.801Z" }, + { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701, upload-time = "2025-11-26T02:36:09.625Z" }, + { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544, upload-time = "2025-11-26T02:34:46.195Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020, upload-time = "2025-11-26T02:34:31.085Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735, upload-time = "2025-11-26T02:34:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779, upload-time = "2025-11-26T02:32:55.109Z" }, + { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755, upload-time = "2025-11-26T02:33:11.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732, upload-time = "2025-11-26T02:33:27.122Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571, upload-time = "2025-11-26T02:33:42.986Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440, upload-time = "2025-11-26T02:34:15.439Z" }, + { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424, upload-time = "2025-11-26T02:35:02.742Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675, upload-time = "2025-11-26T02:35:20.252Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098, upload-time = "2025-11-26T02:35:37.643Z" }, + { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397, upload-time = "2025-11-26T02:35:56.215Z" }, ] [[package]] name = "fastjsonschema" version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, ] [[package]] name = "filelock" -version = "3.24.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/92/a8e2479937ff39185d20dd6a851c1a63e55849e447a55e798cc2e1f49c65/filelock-3.24.3.tar.gz", hash = "sha256:011a5644dc937c22699943ebbfc46e969cdde3e171470a6e40b9533e5a72affa", size = 37935 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/0f/5d0c71a1aefeb08efff26272149e07ab922b64f46c63363756224bd6872e/filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d", size = 24331 }, -] - -[[package]] -name = "fire" -version = "0.7.1" +version = "3.25.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "termcolor" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720 } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945 }, + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, ] [[package]] @@ -908,69 +913,77 @@ dependencies = [ { name = "transformers", extra = ["sentencepiece"] }, { name = "wikipedia-api" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607 } +sdist = { url = "https://files.pythonhosted.org/packages/70/bf/f6cc2ee7fd15946fef5f7747327eeed49837a3d7fd34460129bc936f0de7/flair-0.15.1.tar.gz", hash = "sha256:780b4eeffba044c4a181f1810872fc01201c2eaed8d2ef4bcb126a6464e91933", size = 388607, upload-time = "2025-02-05T14:45:44.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604, upload-time = "2025-02-05T14:45:41.788Z" }, +] + +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/b9/da0f10de728204eee8b582356a2dfab34bc02b1102fec061656d8db44630/flair-0.15.1-py3-none-any.whl", hash = "sha256:3b6b793f2380cd618e988e7b16fbadcec6502aaa8f11a0890390160303aed553", size = 1174604 }, + { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, ] [[package]] name = "fonttools" -version = "4.61.1" +version = "4.62.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/96/686339e0fda8142b7ebed39af53f4a5694602a729662f42a6209e3be91d0/fonttools-4.62.0.tar.gz", hash = "sha256:0dc477c12b8076b4eb9af2e440421b0433ffa9e1dcb39e0640a6c94665ed1098", size = 3579521, upload-time = "2026-03-09T16:50:06.217Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799 }, - { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032 }, - { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863 }, - { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076 }, - { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623 }, - { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327 }, - { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180 }, - { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654 }, - { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996 }, + { url = "https://files.pythonhosted.org/packages/82/e0/9db48ec7f6b95bae7b20667ded54f18dba8e759ef66232c8683822ae26fc/fonttools-4.62.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:62b6a3d0028e458e9b59501cf7124a84cd69681c433570e4861aff4fb54a236c", size = 2873527, upload-time = "2026-03-09T16:48:12.416Z" }, + { url = "https://files.pythonhosted.org/packages/dd/45/86eccfdc922cb9fafc63189a9793fa9f6dd60e68a07be42e454ef2c0deae/fonttools-4.62.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:966557078b55e697f65300b18025c54e872d7908d1899b7314d7c16e64868cb2", size = 2417427, upload-time = "2026-03-09T16:48:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/d3/98/f547a1fceeae81a9a5c6461bde2badac8bf50bda7122a8012b32b1e65396/fonttools-4.62.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf34861145b516cddd19b07ae6f4a61ea1c6326031b960ec9ddce8ee815e888", size = 4934993, upload-time = "2026-03-09T16:48:18.186Z" }, + { url = "https://files.pythonhosted.org/packages/5c/57/a23a051fcff998fdfabdd33c6721b5bad499da08b586d3676993410071f0/fonttools-4.62.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e2ff573de2775508c8a366351fb901c4ced5dc6cf2d87dd15c973bedcdd5216", size = 4892154, upload-time = "2026-03-09T16:48:20.736Z" }, + { url = "https://files.pythonhosted.org/packages/e2/62/e27644b433dc6db1d47bc6028a27d772eec5cc8338e24a9a1fce5d7120aa/fonttools-4.62.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:55b189a1b3033860a38e4e5bd0626c5aa25c7ce9caee7bc784a8caec7a675401", size = 4911635, upload-time = "2026-03-09T16:48:23.174Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e2/1bf141911a5616bacfe9cf237c80ccd69d0d92482c38c0f7f6a55d063ad9/fonttools-4.62.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:825f98cd14907c74a4d0a3f7db8570886ffce9c6369fed1385020febf919abf6", size = 5031492, upload-time = "2026-03-09T16:48:25.095Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/790c292f4347ecfa77d9c7e0d1d91e04ab227f6e4a337ed4fe37ca388048/fonttools-4.62.0-cp310-cp310-win32.whl", hash = "sha256:c858030560f92a054444c6e46745227bfd3bb4e55383c80d79462cd47289e4b5", size = 1507656, upload-time = "2026-03-09T16:48:26.973Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ee/08c0b7f8bac6e44638de6fe9a3e710a623932f60eccd58912c4d4743516d/fonttools-4.62.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bf75eb69330e34ad2a096fac67887102c8537991eb6cac1507fc835bbb70e0a", size = 1556540, upload-time = "2026-03-09T16:48:30.359Z" }, + { url = "https://files.pythonhosted.org/packages/9c/57/c2487c281dde03abb2dec244fd67059b8d118bd30a653cbf69e94084cb23/fonttools-4.62.0-py3-none-any.whl", hash = "sha256:75064f19a10c50c74b336aa5ebe7b1f89fd0fb5255807bfd4b0c6317098f4af3", size = 1152427, upload-time = "2026-03-09T16:50:04.074Z" }, ] [[package]] name = "fqdn" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, ] [[package]] name = "frozenlist" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230 }, - { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621 }, - { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889 }, - { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464 }, - { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649 }, - { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188 }, - { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748 }, - { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351 }, - { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767 }, - { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887 }, - { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785 }, - { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312 }, - { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650 }, - { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659 }, - { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837 }, - { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989 }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] [[package]] name = "fsspec" -version = "2025.10.0" +version = "2026.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285 } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966 }, + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, ] [package.optional-dependencies] @@ -985,9 +998,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, ] [[package]] @@ -1000,49 +1013,49 @@ dependencies = [ { name = "requests", extra = ["socks"] }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/cf/919a9fa16faf8e4572a24d941353edaf4d54e3ddcd048e6c1aeb8c7a9903/gdown-5.2.1.tar.gz", hash = "sha256:247c2ad1f579db5b66b54c04e6a871995fc8fd7021708b950b8ba7b32cf90323", size = 284743 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/cf/919a9fa16faf8e4572a24d941353edaf4d54e3ddcd048e6c1aeb8c7a9903/gdown-5.2.1.tar.gz", hash = "sha256:247c2ad1f579db5b66b54c04e6a871995fc8fd7021708b950b8ba7b32cf90323", size = 284743, upload-time = "2026-01-11T09:34:01.037Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/21/35dd0a0b7428bd67b12b358d7b4277f693493a3839b071d540a4c8357b78/gdown-5.2.1-py3-none-any.whl", hash = "sha256:391f0480d495fb87644d1a1ee3ddfeb2144e1de31408fbc74f7e3b3ba927052b", size = 18241 }, + { url = "https://files.pythonhosted.org/packages/87/21/35dd0a0b7428bd67b12b358d7b4277f693493a3839b071d540a4c8357b78/gdown-5.2.1-py3-none-any.whl", hash = "sha256:391f0480d495fb87644d1a1ee3ddfeb2144e1de31408fbc74f7e3b3ba927052b", size = 18241, upload-time = "2026-01-11T09:34:02.637Z" }, ] [[package]] name = "greenlet" version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747 }, - { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202 }, - { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620 }, - { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132 }, - { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729 }, - { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946 }, - { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494 }, - { url = "https://files.pythonhosted.org/packages/ac/78/f93e840cbaef8becaf6adafbaf1319682a6c2d8c1c20224267a5c6c8c891/greenlet-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f", size = 230092 }, + { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747, upload-time = "2026-02-20T20:16:21.325Z" }, + { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202, upload-time = "2026-02-20T20:47:28.955Z" }, + { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620, upload-time = "2026-02-20T20:55:55.581Z" }, + { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729, upload-time = "2026-02-20T20:20:58.395Z" }, + { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946, upload-time = "2026-02-20T20:49:31.102Z" }, + { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494, upload-time = "2026-02-20T20:21:06.541Z" }, + { url = "https://files.pythonhosted.org/packages/ac/78/f93e840cbaef8becaf6adafbaf1319682a6c2d8c1c20224267a5c6c8c891/greenlet-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f", size = 230092, upload-time = "2026-02-20T20:17:09.379Z" }, ] [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "hf-xet" -version = "1.2.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020 } +sdist = { url = "https://files.pythonhosted.org/packages/68/01/928fd82663fb0ab455551a178303a2960e65029da66b21974594f3a20a94/hf_xet-1.4.0.tar.gz", hash = "sha256:48e6ba7422b0885c9bbd8ac8fdf5c4e1306c3499b82d489944609cc4eae8ecbd", size = 660350, upload-time = "2026-03-11T18:50:03.354Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099 }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178 }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214 }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054 }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812 }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920 }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 }, + { url = "https://files.pythonhosted.org/packages/9f/f9/a0b01945726aea81d2f213457cd5f5102a51e6fd1ca9f9769f561fb57501/hf_xet-1.4.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:981d2b5222c3baadf9567c135cf1d1073786f546b7745686978d46b5df179e16", size = 3799223, upload-time = "2026-03-11T18:49:49.884Z" }, + { url = "https://files.pythonhosted.org/packages/5d/30/ee62b0c00412f49a7e6f509f0104ee8808692278d247234336df48029349/hf_xet-1.4.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:cc8bd050349d0d7995ce7b3a3a18732a2a8062ce118a82431602088abb373428", size = 3560682, upload-time = "2026-03-11T18:49:48.633Z" }, + { url = "https://files.pythonhosted.org/packages/93/d0/0fe5c44dbced465a651a03212e1135d0d7f95d19faada692920cb56f8e38/hf_xet-1.4.0-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5d0c38d2a280d814280b8c15eead4a43c9781e7bf6fc37843cffab06dcdc76b9", size = 4218323, upload-time = "2026-03-11T18:49:40.921Z" }, + { url = "https://files.pythonhosted.org/packages/73/df/7b3c99a4e50442039eae498e5c23db634538eb3e02214109880cf1165d4c/hf_xet-1.4.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6a883f0250682ea888a1bd0af0631feda377e59ad7aae6fb75860ecee7ae0f93", size = 3997156, upload-time = "2026-03-11T18:49:39.634Z" }, + { url = "https://files.pythonhosted.org/packages/a9/26/47dfedf271c21d95346660ae1698e7ece5ab10791fa6c4f20c59f3713083/hf_xet-1.4.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:99e1d9255fe8ecdf57149bb0543d49e7b7bd8d491ddf431eb57e114253274df5", size = 4199052, upload-time = "2026-03-11T18:49:57.097Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c0/346b9aad1474e881e65f998d5c1981695f0af045bc7a99204d9d86759a89/hf_xet-1.4.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b25f06ce42bd2d5f2e79d4a2d72f783d3ac91827c80d34a38cf8e5290dd717b0", size = 4434346, upload-time = "2026-03-11T18:49:58.67Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d6/88ce9d6caa397c3b935263d5bcbe3ebf6c443f7c76098b8c523d206116b9/hf_xet-1.4.0-cp37-abi3-win_amd64.whl", hash = "sha256:8d6d7816d01e0fa33f315c8ca21b05eca0ce4cdc314f13b81d953e46cc6db11d", size = 3678921, upload-time = "2026-03-11T18:50:09.496Z" }, + { url = "https://files.pythonhosted.org/packages/65/eb/17d99ed253b28a9550ca479867c66a8af4c9bcd8cdc9a26b0c8007c2000a/hf_xet-1.4.0-cp37-abi3-win_arm64.whl", hash = "sha256:cb8d9549122b5b42f34b23b14c6b662a88a586a919d418c774d8dbbc4b3ce2aa", size = 3541054, upload-time = "2026-03-11T18:50:07.963Z" }, ] [[package]] @@ -1053,24 +1066,24 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] name = "httptools" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531 }, - { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408 }, - { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889 }, - { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460 }, - { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267 }, - { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429 }, - { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173 }, + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" }, ] [[package]] @@ -1083,9 +1096,9 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] @@ -1102,27 +1115,48 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782, upload-time = "2026-02-06T09:24:13.098Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395 }, + { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395, upload-time = "2026-02-06T09:24:11.133Z" }, +] + +[[package]] +name = "humanfriendly" +version = "10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyreadline3", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, ] [[package]] name = "identify" -version = "2.6.16" +version = "2.6.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360 } +sdist = { url = "https://files.pythonhosted.org/packages/57/84/376a3b96e5a8d33a7aa2c5b3b31a4b3c364117184bf0b17418055f6ace66/identify-2.6.17.tar.gz", hash = "sha256:f816b0b596b204c9fdf076ded172322f2723cf958d02f9c3587504834c8ff04d", size = 99579, upload-time = "2026-03-01T20:04:12.702Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202 }, + { url = "https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl", hash = "sha256:be5f8412d5ed4b20f2bd41a65f920990bdccaa6a4a18a08f1eefdcd0bdd885f0", size = 99382, upload-time = "2026-03-01T20:04:11.439Z" }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] [[package]] @@ -1132,9 +1166,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/53/c3/b2afa612aa0373f3e6bb190e6de35f293b307d1537f109e3e25dbfcdf212/intervaltree-3.2.1.tar.gz", hash = "sha256:f3f7e8baeb7dd75b9f7a6d33cf3ec10025984a8e66e3016d537e52130c73cfe2", size = 1231531 } +sdist = { url = "https://files.pythonhosted.org/packages/53/c3/b2afa612aa0373f3e6bb190e6de35f293b307d1537f109e3e25dbfcdf212/intervaltree-3.2.1.tar.gz", hash = "sha256:f3f7e8baeb7dd75b9f7a6d33cf3ec10025984a8e66e3016d537e52130c73cfe2", size = 1231531, upload-time = "2025-12-24T04:25:06.773Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/7f/8a80a1c7c2ed05822b5a2b312d2995f30c533641f8198366ba2e26a7bb03/intervaltree-3.2.1-py2.py3-none-any.whl", hash = "sha256:a8a8381bbd35d48ceebee932c77ffc988492d22fb1d27d0ba1d74a7694eb8f0b", size = 25929 }, + { url = "https://files.pythonhosted.org/packages/83/7f/8a80a1c7c2ed05822b5a2b312d2995f30c533641f8198366ba2e26a7bb03/intervaltree-3.2.1-py2.py3-none-any.whl", hash = "sha256:a8a8381bbd35d48ceebee932c77ffc988492d22fb1d27d0ba1d74a7694eb8f0b", size = 25929, upload-time = "2025-12-24T04:25:05.298Z" }, ] [[package]] @@ -1142,7 +1176,7 @@ name = "ipykernel" version = "7.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "platform_system == 'Darwin'" }, + { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, { name = "ipython" }, @@ -1156,9 +1190,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046, upload-time = "2026-02-06T16:43:27.403Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788 }, + { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788, upload-time = "2026-02-06T16:43:25.149Z" }, ] [[package]] @@ -1178,9 +1212,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813 }, + { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, ] [[package]] @@ -1194,9 +1228,9 @@ dependencies = [ { name = "traitlets" }, { name = "widgetsnbextension" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, ] [[package]] @@ -1206,9 +1240,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "arrow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, ] [[package]] @@ -1218,9 +1252,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parso" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, ] [[package]] @@ -1230,9 +1264,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] @@ -1243,36 +1277,36 @@ dependencies = [ { name = "click" }, { name = "rapidfuzz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537 } +sdist = { url = "https://files.pythonhosted.org/packages/42/37/99f800e78b9cff2b6c404812e33641b6b6b5b94088081e6a28548094e46f/jiwer-3.0.5.tar.gz", hash = "sha256:4a215a774b6a2ad92838bfc9f64f072709557af48e3eb9d6bdbcee6819535b2d", size = 17537, upload-time = "2024-11-01T16:18:57.337Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/00bec152973661ea89628490367fa3058d7f56d51d2c1ad02a44589d12cd/jiwer-3.0.5-py3-none-any.whl", hash = "sha256:5a55758fd1ed0b46a04e51eae6325ad77810511d1372dcbb2333ec8d5850f7b2", size = 21990, upload-time = "2024-11-01T16:18:55.928Z" }, ] [[package]] name = "jmespath" version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419 }, + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, ] [[package]] name = "joblib" version = "1.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] [[package]] name = "json5" version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441 } +sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441, upload-time = "2026-01-01T19:42:14.99Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163 }, + { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163, upload-time = "2026-01-01T19:42:13.962Z" }, ] [[package]] @@ -1282,18 +1316,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359 } +sdist = { url = "https://files.pythonhosted.org/packages/35/87/bcda8e46c88d0e34cad2f09ee2d0c7f5957bccdb9791b0b934ec84d84be4/jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74", size = 11359, upload-time = "2023-09-01T12:34:44.187Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701 }, + { url = "https://files.pythonhosted.org/packages/f8/62/d9ba6323b9202dd2fe166beab8a86d29465c41a0288cbe229fac60c1ab8d/jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55", size = 8701, upload-time = "2023-09-01T12:34:42.563Z" }, ] [[package]] name = "jsonpointer" version = "3.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, ] [[package]] @@ -1306,9 +1340,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] [package.optional-dependencies] @@ -1331,9 +1365,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] [[package]] @@ -1348,9 +1382,9 @@ dependencies = [ { name = "nbconvert" }, { name = "notebook" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, ] [[package]] @@ -1364,9 +1398,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020 } +sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371 }, + { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, ] [[package]] @@ -1383,9 +1417,9 @@ dependencies = [ { name = "pyzmq" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, ] [[package]] @@ -1396,9 +1430,9 @@ dependencies = [ { name = "platformdirs" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, ] [[package]] @@ -1415,9 +1449,9 @@ dependencies = [ { name = "rfc3986-validator" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430 }, + { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, ] [[package]] @@ -1427,9 +1461,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823, upload-time = "2025-08-27T17:47:34.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687 }, + { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687, upload-time = "2025-08-27T17:47:33.15Z" }, ] [[package]] @@ -1457,9 +1491,9 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949, upload-time = "2025-08-21T14:42:54.042Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221 }, + { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221, upload-time = "2025-08-21T14:42:52.034Z" }, ] [[package]] @@ -1470,14 +1504,14 @@ dependencies = [ { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "terminado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770, upload-time = "2026-01-14T16:53:20.213Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704 }, + { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704, upload-time = "2026-01-14T16:53:18.738Z" }, ] [[package]] name = "jupyterlab" -version = "4.5.4" +version = "4.5.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1495,18 +1529,18 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/6b/21af7c0512bdf67e0c54c121779a1f2a97a164a7657e13fced79db8fa5a0/jupyterlab-4.5.4.tar.gz", hash = "sha256:c215f48d8e4582bd2920ad61cc6a40d8ebfef7e5a517ae56b8a9413c9789fdfb", size = 23943597 } +sdist = { url = "https://files.pythonhosted.org/packages/ac/d5/730628e03fff2e8a8e8ccdaedde1489ab1309f9a4fa2536248884e30b7c7/jupyterlab-4.5.6.tar.gz", hash = "sha256:642fe2cfe7f0f5922a8a558ba7a0d246c7bc133b708dfe43f7b3a826d163cf42", size = 23970670, upload-time = "2026-03-11T14:17:04.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/9f/a70972ece62ead2d81acc6223188f6d18a92f665ccce17796a0cdea4fcf5/jupyterlab-4.5.4-py3-none-any.whl", hash = "sha256:cc233f70539728534669fb0015331f2a3a87656207b3bb2d07916e9289192f12", size = 12391867 }, + { url = "https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl", hash = "sha256:d6b3dac883aa4d9993348e0f8e95b24624f75099aed64eab6a4351a9cdd1e580", size = 12447124, upload-time = "2026-03-11T14:17:00.229Z" }, ] [[package]] name = "jupyterlab-pygments" version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, ] [[package]] @@ -1522,44 +1556,44 @@ dependencies = [ { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, ] [[package]] name = "jupyterlab-widgets" version = "3.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, ] [[package]] name = "kiwisolver" -version = "1.4.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159 }, - { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578 }, - { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312 }, - { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458 }, - { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640 }, - { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074 }, - { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036 }, - { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310 }, - { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943 }, - { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488 }, - { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787 }, - { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730 }, - { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036 }, - { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183 }, - { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675 }, - { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277 }, - { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994 }, - { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744 }, +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", size = 122802, upload-time = "2026-03-09T13:12:37.515Z" }, + { url = "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", size = 66216, upload-time = "2026-03-09T13:12:38.812Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", size = 63917, upload-time = "2026-03-09T13:12:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", size = 1628776, upload-time = "2026-03-09T13:12:41.976Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", size = 1228164, upload-time = "2026-03-09T13:12:44.002Z" }, + { url = "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", size = 1246656, upload-time = "2026-03-09T13:12:45.557Z" }, + { url = "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", size = 1295562, upload-time = "2026-03-09T13:12:47.562Z" }, + { url = "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", size = 2178473, upload-time = "2026-03-09T13:12:50.254Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", size = 2274035, upload-time = "2026-03-09T13:12:51.785Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", size = 2443217, upload-time = "2026-03-09T13:12:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", size = 2249196, upload-time = "2026-03-09T13:12:55.057Z" }, + { url = "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", size = 73389, upload-time = "2026-03-09T13:12:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", size = 64782, upload-time = "2026-03-09T13:12:57.609Z" }, + { url = "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", size = 59606, upload-time = "2026-03-09T13:15:40.81Z" }, + { url = "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", size = 57537, upload-time = "2026-03-09T13:15:42.071Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", size = 79888, upload-time = "2026-03-09T13:15:43.317Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", size = 77584, upload-time = "2026-03-09T13:15:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", size = 73390, upload-time = "2026-03-09T13:15:45.832Z" }, ] [[package]] @@ -1569,57 +1603,45 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474, upload-time = "2021-05-07T07:54:13.562Z" } [[package]] name = "lark" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, -] - -[[package]] -name = "lightning-utilities" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fire" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b5/ee/4b206e722c4fc138436aa91299692bba4fdad1b6ce8429bf291929456b04/lightning-utilities-0.3.0.tar.gz", hash = "sha256:d769ab9b76ebdee3243d1051d509aafee57d7947734ddc22977deef8a6427f2f", size = 15292 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/fc/1f4ff2bcba4e6162276cabe831a431ef14681a7158e693a5cf828dd6fa1b/lightning_utilities-0.3.0-py3-none-any.whl", hash = "sha256:1ae9bdd8f1be3c81b1ac4820f6eeddcbafcc2505c57a5940054466f4763bc22d", size = 15594 }, + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, ] [[package]] name = "lxml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589 }, - { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671 }, - { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961 }, - { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087 }, - { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620 }, - { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664 }, - { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397 }, - { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178 }, - { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148 }, - { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035 }, - { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111 }, - { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662 }, - { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973 }, - { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953 }, - { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695 }, - { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051 }, - { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264 }, - { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435 }, - { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913 }, - { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357 }, - { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295 }, - { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913 }, +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589, upload-time = "2025-09-22T04:00:10.51Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671, upload-time = "2025-09-22T04:00:15.411Z" }, + { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961, upload-time = "2025-09-22T04:00:17.619Z" }, + { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087, upload-time = "2025-09-22T04:00:19.868Z" }, + { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620, upload-time = "2025-09-22T04:00:21.877Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664, upload-time = "2025-09-22T04:00:23.714Z" }, + { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397, upload-time = "2025-09-22T04:00:25.544Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178, upload-time = "2025-09-22T04:00:27.602Z" }, + { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148, upload-time = "2025-09-22T04:00:29.323Z" }, + { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035, upload-time = "2025-09-22T04:00:31.061Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111, upload-time = "2025-09-22T04:00:33.11Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662, upload-time = "2025-09-22T04:00:35.237Z" }, + { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973, upload-time = "2025-09-22T04:00:37.086Z" }, + { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953, upload-time = "2025-09-22T04:00:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695, upload-time = "2025-09-22T04:00:41.402Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051, upload-time = "2025-09-22T04:00:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264, upload-time = "2025-09-22T04:04:32.892Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435, upload-time = "2025-09-22T04:04:34.907Z" }, + { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913, upload-time = "2025-09-22T04:04:37.205Z" }, + { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357, upload-time = "2025-09-22T04:04:39.322Z" }, + { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295, upload-time = "2025-09-22T04:04:41.647Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913, upload-time = "2025-09-22T04:04:43.602Z" }, ] [[package]] @@ -1629,9 +1651,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, ] [[package]] @@ -1641,28 +1663,28 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631 }, - { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057 }, - { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050 }, - { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681 }, - { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705 }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524 }, - { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282 }, - { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745 }, - { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571 }, - { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056 }, - { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932 }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, ] [[package]] @@ -1680,17 +1702,17 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828 }, - { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050 }, - { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452 }, - { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928 }, - { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377 }, - { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127 }, - { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252 }, - { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693 }, - { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205 }, + { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, + { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, + { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, ] [[package]] @@ -1700,18 +1722,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516 }, + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] @@ -1721,18 +1743,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467, upload-time = "2025-12-23T11:36:34.994Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598 }, + { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" }, ] [[package]] name = "more-itertools" version = "10.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667 }, + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, ] [[package]] @@ -1743,18 +1765,18 @@ dependencies = [ { name = "jinja2" }, { name = "matplotlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b2/9943f3e0bdf4ff9968b7ac4499152162d8ee8fc8eaa55487339872f86300/mpld3-0.5.12.tar.gz", hash = "sha256:1333e2bca012ea9af3c27801ba36f65bc26540b6fad4c56a903afb19477f2c37", size = 1028433, upload-time = "2025-11-05T18:12:24.183Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051 }, + { url = "https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl", hash = "sha256:bea31799a4041029a906f53f2662bbf1c49903e0c0bc712b412354158ec7cf54", size = 203051, upload-time = "2025-11-05T18:12:22.527Z" }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] [[package]] @@ -1764,27 +1786,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176 }, - { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996 }, - { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631 }, - { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561 }, - { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223 }, - { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322 }, - { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005 }, - { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173 }, - { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273 }, - { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956 }, - { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477 }, - { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615 }, - { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930 }, - { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807 }, - { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103 }, - { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416 }, - { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022 }, - { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238 }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319 }, +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] [[package]] @@ -1794,23 +1816,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503 } +sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503, upload-time = "2025-04-17T03:11:27.742Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083 }, - { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128 }, - { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132 }, - { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948 }, - { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636 }, - { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478 }, + { url = "https://files.pythonhosted.org/packages/c8/f8/7f9a8f08bf98cea1dfaa181e05cc8bbcb59cecf044b5a9ac3cce39f9c449/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df", size = 135083, upload-time = "2025-04-17T03:11:04.223Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/b7b10dbfc17b2b3ce07d4d30b3ba8367d0ed32d6d46cd166e298f161dd46/multiprocess-0.70.18-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213", size = 135128, upload-time = "2025-04-17T03:11:06.045Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a3/5f8d3b9690ea5580bee5868ab7d7e2cfca74b7e826b28192b40aa3881cdc/multiprocess-0.70.18-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2", size = 135132, upload-time = "2025-04-17T03:11:07.533Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948, upload-time = "2025-04-17T03:11:20.223Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636, upload-time = "2025-04-17T03:11:24.936Z" }, + { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z" }, ] [[package]] name = "narwhals" -version = "2.17.0" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/59/81d0f4cad21484083466f278e6b392addd9f4205b48d45b5c8771670ebf8/narwhals-2.17.0.tar.gz", hash = "sha256:ebd5bc95bcfa2f8e89a8ac09e2765a63055162837208e67b42d6eeb6651d5e67", size = 620306 } +sdist = { url = "https://files.pythonhosted.org/packages/47/b4/02a8add181b8d2cd5da3b667cd102ae536e8c9572ab1a130816d70a89edb/narwhals-2.18.0.tar.gz", hash = "sha256:1de5cee338bc17c338c6278df2c38c0dd4290499fcf70d75e0a51d5f22a6e960", size = 620222, upload-time = "2026-03-10T15:51:27.14Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl", hash = "sha256:2ac5307b7c2b275a7d66eeda906b8605e3d7a760951e188dcfff86e8ebe083dd", size = 444897 }, + { url = "https://files.pythonhosted.org/packages/fe/75/0b4a10da17a44cf13567d08a9c7632a285297e46253263f1ae119129d10a/narwhals-2.18.0-py3-none-any.whl", hash = "sha256:68378155ee706ac9c5b25868ef62ecddd62947b6df7801a0a156bc0a615d2d0d", size = 444865, upload-time = "2026-03-10T15:51:24.085Z" }, ] [[package]] @@ -1823,9 +1845,9 @@ dependencies = [ { name = "nbformat" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554 } +sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554, upload-time = "2025-12-23T07:45:46.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465 }, + { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465, upload-time = "2025-12-23T07:45:44.51Z" }, ] [[package]] @@ -1848,9 +1870,9 @@ dependencies = [ { name = "pygments" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/47/81f886b699450d0569f7bc551df2b1673d18df7ff25cc0c21ca36ed8a5ff/nbconvert-7.17.0.tar.gz", hash = "sha256:1b2696f1b5be12309f6c7d707c24af604b87dfaf6d950794c7b07acab96dda78", size = 862855 } +sdist = { url = "https://files.pythonhosted.org/packages/38/47/81f886b699450d0569f7bc551df2b1673d18df7ff25cc0c21ca36ed8a5ff/nbconvert-7.17.0.tar.gz", hash = "sha256:1b2696f1b5be12309f6c7d707c24af604b87dfaf6d950794c7b07acab96dda78", size = 862855, upload-time = "2026-01-29T16:37:48.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl", hash = "sha256:4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518", size = 261510 }, + { url = "https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl", hash = "sha256:4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518", size = 261510, upload-time = "2026-01-29T16:37:46.322Z" }, ] [[package]] @@ -1863,9 +1885,9 @@ dependencies = [ { name = "jupyter-core" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, ] [[package]] @@ -1875,41 +1897,41 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nbformat" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/6f/b52c4da26babeb521078c08c78c3187a59197098ffc7a70b0fe76851813a/nbstripout-0.9.1.tar.gz", hash = "sha256:313bbb4217c8e38998567e5d790b6bd6c3a17a8c39073b205b84dadfc5d756dc", size = 32356 } +sdist = { url = "https://files.pythonhosted.org/packages/f9/6f/b52c4da26babeb521078c08c78c3187a59197098ffc7a70b0fe76851813a/nbstripout-0.9.1.tar.gz", hash = "sha256:313bbb4217c8e38998567e5d790b6bd6c3a17a8c39073b205b84dadfc5d756dc", size = 32356, upload-time = "2026-02-21T16:19:55.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl", hash = "sha256:ca027ee45742ee77e4f8e9080254f9a707f1161ba11367b82fdf4a29892c759e", size = 19136 }, + { url = "https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl", hash = "sha256:ca027ee45742ee77e4f8e9080254f9a707f1161ba11367b82fdf4a29892c759e", size = 19136, upload-time = "2026-02-21T16:19:54.868Z" }, ] [[package]] name = "nest-asyncio" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, ] [[package]] name = "networkx" version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, ] [[package]] name = "nodeenv" version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611 } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438 }, + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] [[package]] name = "notebook" -version = "7.5.3" +version = "7.5.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, @@ -1918,9 +1940,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/cb/cc7f4df5cee315dd126a47eb60890690a0438d5e0dd40c32d60ce16de377/notebook-7.5.3.tar.gz", hash = "sha256:393ceb269cf9fdb02a3be607a57d7bd5c2c14604f1818a17dbeb38e04f98cbfa", size = 14073140 } +sdist = { url = "https://files.pythonhosted.org/packages/1f/6d/41052c48d6f6349ca0a7c4d1f6a78464de135e6d18f5829ba2510e62184c/notebook-7.5.5.tar.gz", hash = "sha256:dc0bfab0f2372c8278c457423d3256c34154ac2cc76bf20e9925260c461013c3", size = 14169167, upload-time = "2026-03-11T16:32:51.922Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/98/9286e7f35e5584ebb79f997f2fb0cb66745c86f6c5fccf15ba32aac5e908/notebook-7.5.3-py3-none-any.whl", hash = "sha256:c997bfa1a2a9eb58c9bbb7e77d50428befb1033dd6f02c482922e96851d67354", size = 14481744 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/cbd1deb9f07446241e88f8d5fecccd95b249bca0b4e5482214a4d1714c49/notebook-7.5.5-py3-none-any.whl", hash = "sha256:a7c14dbeefa6592e87f72290ca982e0c10f5bbf3786be2a600fda9da2764a2b8", size = 14578929, upload-time = "2026-03-11T16:32:48.021Z" }, ] [[package]] @@ -1930,25 +1952,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, ] [[package]] @@ -1956,8 +1978,7 @@ name = "nvidia-cublas-cu12" version = "12.8.4.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124 }, - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, ] [[package]] @@ -1965,8 +1986,7 @@ name = "nvidia-cuda-cupti-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318 }, - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, ] [[package]] @@ -1974,8 +1994,7 @@ name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, - { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076 }, + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, ] [[package]] @@ -1983,8 +2002,7 @@ name = "nvidia-cuda-runtime-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265 }, - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, ] [[package]] @@ -1995,8 +2013,7 @@ dependencies = [ { name = "nvidia-cublas-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878 }, - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, ] [[package]] @@ -2007,8 +2024,7 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211 }, - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, ] [[package]] @@ -2016,8 +2032,7 @@ name = "nvidia-cufile-cu12" version = "1.13.1.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, - { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705 }, + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, ] [[package]] @@ -2025,8 +2040,7 @@ name = "nvidia-curand-cu12" version = "10.3.9.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754 }, - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, ] [[package]] @@ -2039,8 +2053,7 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841 }, - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, ] [[package]] @@ -2051,8 +2064,7 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129 }, - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, ] [[package]] @@ -2060,8 +2072,7 @@ name = "nvidia-cusparselt-cu12" version = "0.7.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557 }, - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, ] [[package]] @@ -2069,8 +2080,7 @@ name = "nvidia-nccl-cu12" version = "2.27.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625 }, - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, ] [[package]] @@ -2078,8 +2088,7 @@ name = "nvidia-nvjitlink-cu12" version = "12.8.93" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, - { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204 }, + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, ] [[package]] @@ -2087,8 +2096,7 @@ name = "nvidia-nvshmem-cu12" version = "3.4.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/6a/03aa43cc9bd3ad91553a88b5f6fb25ed6a3752ae86ce2180221962bc2aa5/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15", size = 138936938 }, - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095 }, + { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, ] [[package]] @@ -2096,8 +2104,7 @@ name = "nvidia-nvtx-cu12" version = "12.8.90" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161 }, - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, ] [[package]] @@ -2107,24 +2114,44 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045 } +sdist = { url = "https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec", size = 717045, upload-time = "2020-01-18T16:55:48.852Z" } + +[[package]] +name = "onnxruntime" +version = "1.23.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coloredlogs" }, + { name = "flatbuffers" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "sympy" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/d6/311b1afea060015b56c742f3531168c1644650767f27ef40062569960587/onnxruntime-1.23.2-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:a7730122afe186a784660f6ec5807138bf9d792fa1df76556b27307ea9ebcbe3", size = 17195934, upload-time = "2025-10-27T23:06:14.143Z" }, + { url = "https://files.pythonhosted.org/packages/db/db/81bf3d7cecfbfed9092b6b4052e857a769d62ed90561b410014e0aae18db/onnxruntime-1.23.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:b28740f4ecef1738ea8f807461dd541b8287d5650b5be33bca7b474e3cbd1f36", size = 19153079, upload-time = "2025-10-27T23:05:57.686Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4d/a382452b17cf70a2313153c520ea4c96ab670c996cb3a95cc5d5ac7bfdac/onnxruntime-1.23.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f7d1fe034090a1e371b7f3ca9d3ccae2fabae8c1d8844fb7371d1ea38e8e8d2", size = 15219883, upload-time = "2025-10-22T03:46:21.66Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/179bf90679984c85b417664c26aae4f427cba7514bd2d65c43b181b7b08b/onnxruntime-1.23.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ca88747e708e5c67337b0f65eed4b7d0dd70d22ac332038c9fc4635760018f7", size = 17370357, upload-time = "2025-10-22T03:46:57.968Z" }, + { url = "https://files.pythonhosted.org/packages/cd/6d/738e50c47c2fd285b1e6c8083f15dac1a5f6199213378a5f14092497296d/onnxruntime-1.23.2-cp310-cp310-win_amd64.whl", hash = "sha256:0be6a37a45e6719db5120e9986fcd30ea205ac8103fd1fb74b6c33348327a0cc", size = 13467651, upload-time = "2025-10-27T23:06:11.904Z" }, +] [[package]] name = "overrides" version = "7.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, ] [[package]] name = "packaging" version = "26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416 } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366 }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] [[package]] @@ -2137,33 +2164,33 @@ dependencies = [ { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763 }, - { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217 }, - { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791 }, - { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373 }, - { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444 }, - { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459 }, - { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086 }, + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, ] [[package]] name = "pandocfilters" version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, ] [[package]] name = "parso" version = "0.8.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621 } +sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894 }, + { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, ] [[package]] @@ -2173,66 +2200,75 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, ] [[package]] name = "pillow" version = "12.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264 } +sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/30/5bd3d794762481f8c8ae9c80e7b76ecea73b916959eb587521358ef0b2f9/pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0", size = 5304099 }, - { url = "https://files.pythonhosted.org/packages/bd/c1/aab9e8f3eeb4490180e357955e15c2ef74b31f64790ff356c06fb6cf6d84/pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713", size = 4657880 }, - { url = "https://files.pythonhosted.org/packages/f1/0a/9879e30d56815ad529d3985aeff5af4964202425c27261a6ada10f7cbf53/pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b", size = 6222587 }, - { url = "https://files.pythonhosted.org/packages/5a/5f/a1b72ff7139e4f89014e8d451442c74a774d5c43cd938fb0a9f878576b37/pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b", size = 8027678 }, - { url = "https://files.pythonhosted.org/packages/e2/c2/c7cb187dac79a3d22c3ebeae727abee01e077c8c7d930791dc592f335153/pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4", size = 6335777 }, - { url = "https://files.pythonhosted.org/packages/0c/7b/f9b09a7804ec7336effb96c26d37c29d27225783dc1501b7d62dcef6ae25/pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4", size = 7027140 }, - { url = "https://files.pythonhosted.org/packages/98/b2/2fa3c391550bd421b10849d1a2144c44abcd966daadd2f7c12e19ea988c4/pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e", size = 6449855 }, - { url = "https://files.pythonhosted.org/packages/96/ff/9caf4b5b950c669263c39e96c78c0d74a342c71c4f43fd031bb5cb7ceac9/pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff", size = 7151329 }, - { url = "https://files.pythonhosted.org/packages/7b/f8/4b24841f582704da675ca535935bccb32b00a6da1226820845fac4a71136/pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40", size = 6325574 }, - { url = "https://files.pythonhosted.org/packages/f8/f9/9f6b01c0881d7036063aa6612ef04c0e2cad96be21325a1e92d0203f8e91/pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23", size = 7032347 }, - { url = "https://files.pythonhosted.org/packages/79/13/c7922edded3dcdaf10c59297540b72785620abc0538872c819915746757d/pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9", size = 2453457 }, + { url = "https://files.pythonhosted.org/packages/1d/30/5bd3d794762481f8c8ae9c80e7b76ecea73b916959eb587521358ef0b2f9/pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0", size = 5304099, upload-time = "2026-02-11T04:20:06.13Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c1/aab9e8f3eeb4490180e357955e15c2ef74b31f64790ff356c06fb6cf6d84/pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713", size = 4657880, upload-time = "2026-02-11T04:20:09.291Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0a/9879e30d56815ad529d3985aeff5af4964202425c27261a6ada10f7cbf53/pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b", size = 6222587, upload-time = "2026-02-11T04:20:10.82Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5f/a1b72ff7139e4f89014e8d451442c74a774d5c43cd938fb0a9f878576b37/pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b", size = 8027678, upload-time = "2026-02-11T04:20:12.455Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c2/c7cb187dac79a3d22c3ebeae727abee01e077c8c7d930791dc592f335153/pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4", size = 6335777, upload-time = "2026-02-11T04:20:14.441Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7b/f9b09a7804ec7336effb96c26d37c29d27225783dc1501b7d62dcef6ae25/pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4", size = 7027140, upload-time = "2026-02-11T04:20:16.387Z" }, + { url = "https://files.pythonhosted.org/packages/98/b2/2fa3c391550bd421b10849d1a2144c44abcd966daadd2f7c12e19ea988c4/pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e", size = 6449855, upload-time = "2026-02-11T04:20:18.554Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/9caf4b5b950c669263c39e96c78c0d74a342c71c4f43fd031bb5cb7ceac9/pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff", size = 7151329, upload-time = "2026-02-11T04:20:20.646Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f8/4b24841f582704da675ca535935bccb32b00a6da1226820845fac4a71136/pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40", size = 6325574, upload-time = "2026-02-11T04:20:22.43Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/9f6b01c0881d7036063aa6612ef04c0e2cad96be21325a1e92d0203f8e91/pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23", size = 7032347, upload-time = "2026-02-11T04:20:23.932Z" }, + { url = "https://files.pythonhosted.org/packages/79/13/c7922edded3dcdaf10c59297540b72785620abc0538872c819915746757d/pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9", size = 2453457, upload-time = "2026-02-11T04:20:25.392Z" }, ] [[package]] name = "pip" version = "26.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/83/0d7d4e9efe3344b8e2fe25d93be44f64b65364d3c8d7bc6dc90198d5422e/pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8", size = 1812747 } +sdist = { url = "https://files.pythonhosted.org/packages/48/83/0d7d4e9efe3344b8e2fe25d93be44f64b65364d3c8d7bc6dc90198d5422e/pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8", size = 1812747, upload-time = "2026-02-05T02:20:18.702Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", size = 1787723 }, + { url = "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", size = 1787723, upload-time = "2026-02-05T02:20:16.416Z" }, ] [[package]] name = "platformdirs" -version = "4.9.2" +version = "4.9.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/04/fea538adf7dbbd6d186f551d595961e564a3b6715bdf276b477460858672/platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291", size = 28394 } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168 }, + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, ] [[package]] name = "plotly" -version = "6.5.2" +version = "6.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "narwhals" }, { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e3/4f/8a10a9b9f5192cb6fdef62f1d77fa7d834190b2c50c0cd256bd62879212b/plotly-6.5.2.tar.gz", hash = "sha256:7478555be0198562d1435dee4c308268187553cc15516a2f4dd034453699e393", size = 7015695 } +sdist = { url = "https://files.pythonhosted.org/packages/24/fb/41efe84970cfddefd4ccf025e2cbfafe780004555f583e93dba3dac2cdef/plotly-6.6.0.tar.gz", hash = "sha256:b897f15f3b02028d69f755f236be890ba950d0a42d7dfc619b44e2d8cea8748c", size = 7027956, upload-time = "2026-03-02T21:10:25.321Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl", hash = "sha256:8d6daf0f87412e0c0bfe72e809d615217ab57cc715899a1e5145135a7800d1d0", size = 9910315, upload-time = "2026-03-02T21:10:18.131Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl", hash = "sha256:91757653bd9c550eeea2fa2404dba6b85d1e366d54804c340b2c874e5a7eb4a4", size = 9895973 }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "pptree" version = "3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043 } +sdist = { url = "https://files.pythonhosted.org/packages/01/cd/f4f2b79d20a10563d071c38b6ad14bf9c5d75a0edef877d2bed60c024247/pptree-3.1.tar.gz", hash = "sha256:4dd0ba2f58000cbd29d68a5b64bac29bcb5a663642f79404877c0059668a69f6", size = 3043, upload-time = "2020-04-15T18:28:53.362Z" } [[package]] name = "pre-commit" @@ -2245,18 +2281,18 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232 } +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437 }, + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] [[package]] name = "prometheus-client" version = "0.24.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057 }, + { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, ] [[package]] @@ -2266,105 +2302,105 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, ] [[package]] name = "propcache" version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534 }, - { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526 }, - { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263 }, - { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012 }, - { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491 }, - { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319 }, - { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856 }, - { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241 }, - { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552 }, - { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113 }, - { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778 }, - { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047 }, - { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093 }, - { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638 }, - { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229 }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] [[package]] name = "protobuf" -version = "6.33.5" +version = "7.34.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/00/04a2ab36b70a52d0356852979e08b44edde0435f2115dc66e25f2100f3ab/protobuf-7.34.0.tar.gz", hash = "sha256:3871a3df67c710aaf7bb8d214cc997342e63ceebd940c8c7fc65c9b3d697591a", size = 454726, upload-time = "2026-02-27T00:30:25.421Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769 }, - { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118 }, - { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766 }, - { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638 }, - { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411 }, - { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465 }, - { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687 }, + { url = "https://files.pythonhosted.org/packages/13/c4/6322ab5c8f279c4c358bc14eb8aefc0550b97222a39f04eb3c1af7a830fa/protobuf-7.34.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e329966799f2c271d5e05e236459fe1cbfdb8755aaa3b0914fa60947ddea408", size = 429248, upload-time = "2026-02-27T00:30:14.924Z" }, + { url = "https://files.pythonhosted.org/packages/45/99/b029bbbc61e8937545da5b79aa405ab2d9cf307a728f8c9459ad60d7a481/protobuf-7.34.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:9d7a5005fb96f3c1e64f397f91500b0eb371b28da81296ae73a6b08a5b76cdd6", size = 325753, upload-time = "2026-02-27T00:30:17.247Z" }, + { url = "https://files.pythonhosted.org/packages/cc/79/09f02671eb75b251c5550a1c48e7b3d4b0623efd7c95a15a50f6f9fc1e2e/protobuf-7.34.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4a72a8ec94e7a9f7ef7fe818ed26d073305f347f8b3b5ba31e22f81fd85fca02", size = 340200, upload-time = "2026-02-27T00:30:18.672Z" }, + { url = "https://files.pythonhosted.org/packages/b5/57/89727baef7578897af5ed166735ceb315819f1c184da8c3441271dbcfde7/protobuf-7.34.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:964cf977e07f479c0697964e83deda72bcbc75c3badab506fb061b352d991b01", size = 324268, upload-time = "2026-02-27T00:30:20.088Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3e/38ff2ddee5cc946f575c9d8cc822e34bde205cf61acf8099ad88ef19d7d2/protobuf-7.34.0-cp310-abi3-win32.whl", hash = "sha256:f791ec509707a1d91bd02e07df157e75e4fb9fbdad12a81b7396201ec244e2e3", size = 426628, upload-time = "2026-02-27T00:30:21.555Z" }, + { url = "https://files.pythonhosted.org/packages/cb/71/7c32eaf34a61a1bae1b62a2ac4ffe09b8d1bb0cf93ad505f42040023db89/protobuf-7.34.0-cp310-abi3-win_amd64.whl", hash = "sha256:9f9079f1dde4e32342ecbd1c118d76367090d4aaa19da78230c38101c5b3dd40", size = 437901, upload-time = "2026-02-27T00:30:22.836Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e7/14dc9366696dcb53a413449881743426ed289d687bcf3d5aee4726c32ebb/protobuf-7.34.0-py3-none-any.whl", hash = "sha256:e3b914dd77fa33fa06ab2baa97937746ab25695f389869afdf03e81f34e45dc7", size = 170716, upload-time = "2026-02-27T00:30:23.994Z" }, ] [[package]] name = "psutil" version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565 } +sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565, upload-time = "2024-10-17T21:31:45.68Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762 }, - { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777 }, - { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259 }, - { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255 }, - { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804 }, - { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386 }, - { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228 }, + { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762, upload-time = "2024-10-17T21:32:05.991Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777, upload-time = "2024-10-17T21:32:07.872Z" }, + { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259, upload-time = "2024-10-17T21:32:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255, upload-time = "2024-10-17T21:32:11.964Z" }, + { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804, upload-time = "2024-10-17T21:32:13.785Z" }, + { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386, upload-time = "2024-10-17T21:32:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228, upload-time = "2024-10-17T21:32:23.88Z" }, ] [[package]] name = "ptyprocess" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, ] [[package]] name = "pure-eval" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] [[package]] name = "pyarrow" version = "23.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336 } +sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/a8/24e5dc6855f50a62936ceb004e6e9645e4219a8065f304145d7fb8a79d5d/pyarrow-23.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56", size = 34307390 }, - { url = "https://files.pythonhosted.org/packages/bc/8e/4be5617b4aaae0287f621ad31c6036e5f63118cfca0dc57d42121ff49b51/pyarrow-23.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c", size = 35853761 }, - { url = "https://files.pythonhosted.org/packages/2e/08/3e56a18819462210432ae37d10f5c8eed3828be1d6c751b6e6a2e93c286a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258", size = 44493116 }, - { url = "https://files.pythonhosted.org/packages/f8/82/c40b68001dbec8a3faa4c08cd8c200798ac732d2854537c5449dc859f55a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2", size = 47564532 }, - { url = "https://files.pythonhosted.org/packages/20/bc/73f611989116b6f53347581b02177f9f620efdf3cd3f405d0e83cdf53a83/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5", size = 48183685 }, - { url = "https://files.pythonhosted.org/packages/b0/cc/6c6b3ecdae2a8c3aced99956187e8302fc954cc2cca2a37cf2111dad16ce/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222", size = 50605582 }, - { url = "https://files.pythonhosted.org/packages/8d/94/d359e708672878d7638a04a0448edf7c707f9e5606cee11e15aaa5c7535a/pyarrow-23.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d", size = 27521148 }, + { url = "https://files.pythonhosted.org/packages/bc/a8/24e5dc6855f50a62936ceb004e6e9645e4219a8065f304145d7fb8a79d5d/pyarrow-23.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56", size = 34307390, upload-time = "2026-02-16T10:08:08.654Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8e/4be5617b4aaae0287f621ad31c6036e5f63118cfca0dc57d42121ff49b51/pyarrow-23.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c", size = 35853761, upload-time = "2026-02-16T10:08:17.811Z" }, + { url = "https://files.pythonhosted.org/packages/2e/08/3e56a18819462210432ae37d10f5c8eed3828be1d6c751b6e6a2e93c286a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258", size = 44493116, upload-time = "2026-02-16T10:08:25.792Z" }, + { url = "https://files.pythonhosted.org/packages/f8/82/c40b68001dbec8a3faa4c08cd8c200798ac732d2854537c5449dc859f55a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2", size = 47564532, upload-time = "2026-02-16T10:08:34.27Z" }, + { url = "https://files.pythonhosted.org/packages/20/bc/73f611989116b6f53347581b02177f9f620efdf3cd3f405d0e83cdf53a83/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5", size = 48183685, upload-time = "2026-02-16T10:08:42.889Z" }, + { url = "https://files.pythonhosted.org/packages/b0/cc/6c6b3ecdae2a8c3aced99956187e8302fc954cc2cca2a37cf2111dad16ce/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222", size = 50605582, upload-time = "2026-02-16T10:08:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/8d/94/d359e708672878d7638a04a0448edf7c707f9e5606cee11e15aaa5c7535a/pyarrow-23.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d", size = 27521148, upload-time = "2026-02-16T10:08:58.077Z" }, ] [[package]] name = "pycparser" version = "3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, ] [[package]] @@ -2377,9 +2413,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [package.optional-dependencies] @@ -2394,37 +2430,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298 }, - { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475 }, - { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815 }, - { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567 }, - { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442 }, - { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956 }, - { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253 }, - { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050 }, - { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178 }, - { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833 }, - { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156 }, - { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378 }, - { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622 }, - { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441 }, - { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291 }, - { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632 }, - { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905 }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495 }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388 }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879 }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017 }, - { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351 }, - { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363 }, - { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615 }, - { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369 }, - { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218 }, - { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951 }, - { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428 }, - { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009 }, +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, ] [[package]] @@ -2435,9 +2463,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226 } +sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296 }, + { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, ] [[package]] @@ -2449,73 +2477,119 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826 } +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929 }, + { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pymupdf" -version = "1.27.1" +version = "1.27.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/0c/40dda0cc4bd2220a2ef75f8c53dd7d8ed1e29681fcb3df75db6ee9677a7e/pymupdf-1.27.1.tar.gz", hash = "sha256:4afbde0769c336717a149ab0de3330dcb75378f795c1a8c5af55c1a628b17d55", size = 85303479 } +sdist = { url = "https://files.pythonhosted.org/packages/a4/fb/d80374ab091ab7ad5a5e7981a45c877ae094db668c1ab4d30f1109a4ec6a/pymupdf-1.27.2.tar.gz", hash = "sha256:37fc9cedeafb40839f86a074d4d9feab725144bdd4bbfd20308ff8957e2b10af", size = 85353104, upload-time = "2026-03-10T12:53:01.697Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/19/fde6ea4712a904b65e8f41124a0e4233879b87a770fe6a8ce857964de6d5/pymupdf-1.27.1-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:bee9f95512f9556dbf2cacfd1413c61b29a55baa07fa7f8fc83d221d8419888a", size = 23986707 }, - { url = "https://files.pythonhosted.org/packages/75/c2/070dff91ad3f1bc16fd6c6ceff23495601fcce4c92d28be534417596418a/pymupdf-1.27.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:3de95a0889395b0966fafd11b94980b7543a816e89dd1c218597a08543ac3415", size = 23263493 }, - { url = "https://files.pythonhosted.org/packages/8e/db/937377f4b3e0fbf6273c17436a49f7db17df1a46b1be9e26653b6fafc0e1/pymupdf-1.27.1-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2c9d9353b840040cbc724341f4095fb7e2cc1a12a9147d0ec1a0a79f5d773147", size = 24317651 }, - { url = "https://files.pythonhosted.org/packages/72/d5/c701cf2d0cdd6e5d6bca3ca9188d7f5d7ce3ae67dd1368d658cd4bae2707/pymupdf-1.27.1-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:aeaed76e72cbc061149a825ab0811c5f4752970c56591c2938c5042ec06b26e1", size = 24945742 }, - { url = "https://files.pythonhosted.org/packages/2b/29/690202b38b93cf77b73a29c25a63a2b6f3fcb36b1f75006e50b8dee7c108/pymupdf-1.27.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4f1837554134fb45d390a44de8844b2ca9b6c901c82ccc90b340e3b7f3b126ca", size = 25167965 }, - { url = "https://files.pythonhosted.org/packages/8a/81/f937e6aa606fd263c3a45d0ff0f0bbdbf3fb779933091fc0f6179513cc93/pymupdf-1.27.1-cp310-abi3-win32.whl", hash = "sha256:fa33b512d82c6c4852edadf57f22d5f27d16243bb33dac0fbe4eb0f281c5b17e", size = 18006253 }, - { url = "https://files.pythonhosted.org/packages/3e/99/fe4a7752990bf65277718fffbead4478de9afd1c7288d7a6d643f79a6fa7/pymupdf-1.27.1-cp310-abi3-win_amd64.whl", hash = "sha256:4b6268dff3a9d713034eba5c2ffce0da37c62443578941ac5df433adcde57b2f", size = 19236703 }, + { url = "https://files.pythonhosted.org/packages/98/ee/2c10b6bde83ee42f5150b690ace952a802a7e632776dadd42bbfe5b68601/pymupdf-1.27.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:a60ff9010d7025428e31d92ac2c9b4218c7c4844409d0b31a050565ea0a955fd", size = 23987468, upload-time = "2026-03-10T12:37:06.593Z" }, + { url = "https://files.pythonhosted.org/packages/44/06/c8cc8c8ade83f5a75ac0f543edc2bc3c52d8c38c1d55d1e0713558258540/pymupdf-1.27.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:5095efb242cfe1c46fec1c864a13f000098564829c98366582dde7ad9e61aa32", size = 23262964, upload-time = "2026-03-10T12:37:23.915Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8e/df2ab91a680a77c82bc4501cdca60767b3758d75552e4d2849647a16cbc0/pymupdf-1.27.2-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1081235fcfad268d801cd73a7b69c629939e2c46ed4d97035cb1bb7b5b90dc54", size = 24318675, upload-time = "2026-03-10T12:37:42.249Z" }, + { url = "https://files.pythonhosted.org/packages/ab/56/c6c16fa2dcfe2476ec28a9aaaca773dc35c593699e81e573211c91442770/pymupdf-1.27.2-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:917f4dd52daea504d5c60e1430c17d637b5014a43e66d068b4b356effe087dba", size = 24947974, upload-time = "2026-03-10T12:38:00.779Z" }, + { url = "https://files.pythonhosted.org/packages/7b/4f/1659f1d80b5d2f5aad134c2ca63894c63daf47a3ffb7e18987fe25e49097/pymupdf-1.27.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9617d5e71c334937c804544fa201946c5f73d0a97b5842b96857bdabfefbc343", size = 25169417, upload-time = "2026-03-10T12:38:18.912Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/e34d704f7242885dd1d67cfbe1040051a04b4b7e2cf1cbd27af9bd4500a3/pymupdf-1.27.2-cp310-abi3-win32.whl", hash = "sha256:6deef49e06c9a5d8670bf5835a911ab887dac4b3ed4bd60ab7d93da6aa8ff6f1", size = 18008725, upload-time = "2026-03-10T12:38:31.915Z" }, + { url = "https://files.pythonhosted.org/packages/f5/fb/a3f1f8813f6e93c65d1f7ebca6530a889f1ae109229b537f7a617b2aab57/pymupdf-1.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:acdfdb7329882246545a0f6bc85f91739e2773ed81f9301c1687cffb826470f3", size = 19237944, upload-time = "2026-03-10T12:38:45.603Z" }, +] + +[[package]] +name = "pymupdf-layout" +version = "1.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "networkx" }, + { name = "numpy" }, + { name = "onnxruntime" }, + { name = "pymupdf" }, + { name = "pyyaml" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/2e/c0d80ff460babfb91ae708ff2d75a9bbf074322ce4f4ef1afcd911c2f209/pymupdf_layout-1.27.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:bee613779fd1053979b7bfbfb9d2a8937066002fe6981fe9290c5f806d27e44e", size = 15799804, upload-time = "2026-03-10T12:50:59.231Z" }, + { url = "https://files.pythonhosted.org/packages/db/b7/436d0011e0d7e5a94ef23f3ac1f5198f0584f3dd1a29fb1deae2647bd01e/pymupdf_layout-1.27.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:447c2f20524a1cca26dbb30611521758fe464ced7fdedb441c884bf40564e92c", size = 15795181, upload-time = "2026-03-10T12:51:10.582Z" }, + { url = "https://files.pythonhosted.org/packages/2e/f4/9a9a984f172979ee850ccade9d20ad8a9ce0194a517e8b07e69c57c73f7c/pymupdf_layout-1.27.2-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:96767de7f05695d771171ed39077363a280bae6e9d5de70e484a888ae44e3cab", size = 15805201, upload-time = "2026-03-10T12:51:22.472Z" }, + { url = "https://files.pythonhosted.org/packages/be/2d/e0545dcbe65d0e762004c4266f3f272a508a414a9359f452099bf3283dd1/pymupdf_layout-1.27.2-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:962cd43d70b1816df1ac10405bfb3a85545449253f0813a30198829cae64e053", size = 15806229, upload-time = "2026-03-10T12:51:32.981Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b4/f9f7293162a7ce4ccd490241e1e1bd6f415b05809e8f75704cca47e6e32e/pymupdf_layout-1.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:57f63186ab7170dfe1b6918c2ad5c241fdab34a1ed27a75b020306fa29aaf0ec", size = 15809666, upload-time = "2026-03-10T12:51:43.679Z" }, ] [[package]] name = "pymupdf4llm" -version = "0.3.4" +version = "1.27.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pymupdf" }, + { name = "pymupdf-layout" }, { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/9b/97c4fad642f0147af3e884126d80635a1fae02c836fde9c22f0efc053254/pymupdf4llm-0.3.4.tar.gz", hash = "sha256:48d396a5fb3c14351493c7f1dd25b2a843efdbdc4526e489ee100643a2cebec1", size = 74956 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/67/4394de2e5967d80a9b01ec323049163410b3553185c42dea0c346fa27a41/pymupdf4llm-0.3.4-py3-none-any.whl", hash = "sha256:0517492f82af978541162ade20fc54649cdca52acd478e33b97cb6171d69956f", size = 78669 }, + { url = "https://files.pythonhosted.org/packages/e9/23/ac3edfdd7ede9d01895f444b7d4ffffc3f044809e00791808ccc8ea194eb/pymupdf4llm-1.27.2.1-py3-none-any.whl", hash = "sha256:3d1e40d34dbe806e6a8cc7f55999cbd4e5ff5dd8629cfc42d885014b4662ec67", size = 78177, upload-time = "2026-03-10T16:20:02.852Z" }, ] [[package]] name = "pypandoc" version = "1.16.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/18/9f5f70567b97758625335209b98d5cb857e19aa1a9306e9749567a240634/pypandoc-1.16.2.tar.gz", hash = "sha256:7a72a9fbf4a5dc700465e384c3bb333d22220efc4e972cb98cf6fc723cdca86b", size = 31477, upload-time = "2025-11-13T16:30:29.608Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451 }, + { url = "https://files.pythonhosted.org/packages/bb/e9/b145683854189bba84437ea569bfa786f408c8dc5bc16d8eb0753f5583bf/pypandoc-1.16.2-py3-none-any.whl", hash = "sha256:c200c1139c8e3247baf38d1e9279e85d9f162499d1999c6aa8418596558fe79b", size = 19451, upload-time = "2025-11-13T16:30:07.66Z" }, ] [[package]] name = "pyparsing" version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781 }, + { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, ] [[package]] name = "pysocks" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725 }, + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] [[package]] @@ -2525,9 +2599,22 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/7e/9f3b0dd3a074a6c3e1e79f35e465b1f2ee4b262d619de00cfce523cc9b24/python_discovery-1.1.3.tar.gz", hash = "sha256:7acca36e818cd88e9b2ba03e045ad7e93e1713e29c6bbfba5d90202310b7baa5", size = 56945, upload-time = "2026-03-10T15:08:15.038Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, + { url = "https://files.pythonhosted.org/packages/e7/80/73211fc5bfbfc562369b4aa61dc1e4bf07dc7b34df7b317e4539316b809c/python_discovery-1.1.3-py3-none-any.whl", hash = "sha256:90e795f0121bc84572e737c9aa9966311b9fde44ffb88a5953b3ec9b31c6945e", size = 31485, upload-time = "2026-03-10T15:08:13.06Z" }, ] [[package]] @@ -2538,57 +2625,36 @@ dependencies = [ { name = "lxml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987 }, + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, ] [[package]] name = "python-dotenv" -version = "1.2.1" +version = "1.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] [[package]] name = "python-json-logger" version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683 } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548 }, + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, ] [[package]] name = "python-multipart" version = "0.0.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579 }, -] - -[[package]] -name = "pytorch-lightning" -version = "1.8.3.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fsspec", extra = ["http"] }, - { name = "lightning-utilities" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "tensorboardx" }, - { name = "torch" }, - { name = "torchmetrics" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/a3/92533d6b7af907849eb491e867ae488689c4b6207b513ecd31d9febb7a5b/pytorch-lightning-1.8.3.post1.tar.gz", hash = "sha256:4a1804d55c3aa675a2dd21ee17282cad0bc703dfeace70e75dc956f1faf31411", size = 574889 } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/ea/7cb6830785a844eddf45f88b9b14cbf8c3ecce718d4de35503783522f894/pytorch_lightning-1.8.3.post1-py3-none-any.whl", hash = "sha256:2b04cdb876f4e8749161510712b22081dab8db3e6548530608680a415844b6e3", size = 798949 }, + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, ] [[package]] @@ -2599,45 +2665,45 @@ dependencies = [ { name = "numpy" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086 } +sdist = { url = "https://files.pythonhosted.org/packages/90/c3/aecc90deea743b93757e05e235f70eef479509eb3eb11b19f3aa0d528541/pytorch_revgrad-0.2.0.tar.gz", hash = "sha256:9cf097a7d18cbadddeaec9fef74b258d70b6cb8d0c77f524baab18bffc7d7be9", size = 7086, upload-time = "2021-01-09T17:35:49.131Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564 }, + { url = "https://files.pythonhosted.org/packages/ec/e9/10b11b186b99c40213dca68cf6c38051b6704a74e1056d3f3ca4c12f14b9/pytorch_revgrad-0.2.0-py3-none-any.whl", hash = "sha256:2276fb189b2ce26f756a97effe2a6bcf8f7fdc60542c5dfb45c53f09ef123aa7", size = 4564, upload-time = "2021-01-09T17:35:47.543Z" }, ] [[package]] name = "pytz" -version = "2025.2" +version = "2026.1.post1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, + { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, ] [[package]] name = "pywinpty" version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309 } +sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309, upload-time = "2026-02-04T21:51:09.524Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/28/a652709bd76ca7533cd1c443b03add9f5051fdf71bc6bdb8801dddd4e7a3/pywinpty-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:ff05f12d775b142b11c6fe085129bdd759b61cf7d41da6c745e78e3a1ef5bf40", size = 2114320 }, - { url = "https://files.pythonhosted.org/packages/b2/13/a0181cc5c2d5635d3dbc3802b97bc8e3ad4fa7502ccef576651a5e08e54c/pywinpty-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:340ccacb4d74278a631923794ccd758471cfc8eeeeee4610b280420a17ad1e82", size = 235670 }, + { url = "https://files.pythonhosted.org/packages/62/28/a652709bd76ca7533cd1c443b03add9f5051fdf71bc6bdb8801dddd4e7a3/pywinpty-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:ff05f12d775b142b11c6fe085129bdd759b61cf7d41da6c745e78e3a1ef5bf40", size = 2114320, upload-time = "2026-02-04T21:53:50.972Z" }, + { url = "https://files.pythonhosted.org/packages/b2/13/a0181cc5c2d5635d3dbc3802b97bc8e3ad4fa7502ccef576651a5e08e54c/pywinpty-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:340ccacb4d74278a631923794ccd758471cfc8eeeeee4610b280420a17ad1e82", size = 235670, upload-time = "2026-02-04T21:50:20.324Z" }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227 }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019 }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646 }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793 }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293 }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872 }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828 }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415 }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561 }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, ] [[package]] @@ -2647,52 +2713,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850 }, - { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380 }, - { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421 }, - { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149 }, - { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070 }, - { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441 }, - { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529 }, - { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276 }, - { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208 }, - { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766 }, - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, - { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266 }, - { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206 }, - { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747 }, - { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371 }, - { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862 }, +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850, upload-time = "2025-09-08T23:07:26.274Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380, upload-time = "2025-09-08T23:07:29.78Z" }, + { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421, upload-time = "2025-09-08T23:07:31.263Z" }, + { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149, upload-time = "2025-09-08T23:07:33.17Z" }, + { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070, upload-time = "2025-09-08T23:07:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441, upload-time = "2025-09-08T23:07:37.432Z" }, + { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529, upload-time = "2025-09-08T23:07:39.047Z" }, + { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276, upload-time = "2025-09-08T23:07:40.695Z" }, + { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208, upload-time = "2025-09-08T23:07:42.298Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766, upload-time = "2025-09-08T23:07:43.869Z" }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266, upload-time = "2025-09-08T23:09:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206, upload-time = "2025-09-08T23:09:41.902Z" }, + { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747, upload-time = "2025-09-08T23:09:43.741Z" }, + { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371, upload-time = "2025-09-08T23:09:45.575Z" }, + { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862, upload-time = "2025-09-08T23:09:47.448Z" }, ] [[package]] name = "rapidfuzz" version = "3.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900, upload-time = "2025-11-01T11:54:52.321Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376 }, - { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903 }, - { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655 }, - { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708 }, - { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106 }, - { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048 }, - { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020 }, - { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958 }, - { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043 }, - { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273 }, - { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875 }, + { url = "https://files.pythonhosted.org/packages/69/d1/0efa42a602ed466d3ca1c462eed5d62015c3fd2a402199e2c4b87aa5aa25/rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1", size = 1952376, upload-time = "2025-11-01T11:52:29.175Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/37a169bb28b23850a164e6624b1eb299e1ad73c9e7c218ee15744e68d628/rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2", size = 1390903, upload-time = "2025-11-01T11:52:31.239Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/b37207cbbdb6eaafac3da3f55ea85287b27745cb416e75e15769b7d8abe8/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7", size = 1385655, upload-time = "2025-11-01T11:52:32.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/bb/ca53e518acf43430be61f23b9c5987bd1e01e74fcb7a9ee63e00f597aefb/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1", size = 3164708, upload-time = "2025-11-01T11:52:34.618Z" }, + { url = "https://files.pythonhosted.org/packages/df/e1/7667bf2db3e52adb13cb933dd4a6a2efc66045d26fa150fc0feb64c26d61/rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897", size = 1221106, upload-time = "2025-11-01T11:52:36.069Z" }, + { url = "https://files.pythonhosted.org/packages/05/8a/84d9f2d46a2c8eb2ccae81747c4901fa10fe4010aade2d57ce7b4b8e02ec/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9", size = 2406048, upload-time = "2025-11-01T11:52:37.936Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a9/a0b7b7a1b81a020c034eb67c8e23b7e49f920004e295378de3046b0d99e1/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747", size = 2527020, upload-time = "2025-11-01T11:52:39.657Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bc/416df7d108b99b4942ba04dd4cf73c45c3aadb3ef003d95cad78b1d12eb9/rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825", size = 4273958, upload-time = "2025-11-01T11:52:41.017Z" }, + { url = "https://files.pythonhosted.org/packages/81/d0/b81e041c17cd475002114e0ab8800e4305e60837882cb376a621e520d70f/rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9", size = 1725043, upload-time = "2025-11-01T11:52:42.465Z" }, + { url = "https://files.pythonhosted.org/packages/09/6b/64ad573337d81d64bc78a6a1df53a72a71d54d43d276ce0662c2e95a1f35/rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141", size = 1542273, upload-time = "2025-11-01T11:52:44.005Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5e/faf76e259bc15808bc0b86028f510215c3d755b6c3a3911113079485e561/rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923", size = 814875, upload-time = "2025-11-01T11:52:45.405Z" }, ] [[package]] @@ -2704,34 +2770,34 @@ dependencies = [ { name = "rpds-py" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] [[package]] name = "regex" -version = "2026.2.19" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/c0/d8079d4f6342e4cec5c3e7d7415b5cd3e633d5f4124f7a4626908dbe84c7/regex-2026.2.19.tar.gz", hash = "sha256:6fb8cb09b10e38f3ae17cc6dc04a1df77762bd0351b6ba9041438e7cc85ec310", size = 414973 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/de/f10b4506acfd684de4e42b0aa56ccea1a778a18864da8f6d319a40591062/regex-2026.2.19-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f5a37a17d110f9d5357a43aa7e3507cb077bf3143d1c549a45c4649e90e40a70", size = 488369 }, - { url = "https://files.pythonhosted.org/packages/8b/2f/b4eaef1f0b4d0bf2a73eaf07c08f6c13422918a4180c9211ce0521746d0c/regex-2026.2.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:676c4e6847a83a1d5732b4ed553881ad36f0a8133627bb695a89ecf3571499d3", size = 290743 }, - { url = "https://files.pythonhosted.org/packages/76/7c/805413bd0a88d04688c0725c222cfb811bd54a2f571004c24199a1ae55d6/regex-2026.2.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82336faeecac33297cd42857c3b36f12b91810e3fdd276befdd128f73a2b43fa", size = 288652 }, - { url = "https://files.pythonhosted.org/packages/08/ff/2c4cd530a878b1975398e76faef4285f11e7c9ccf1aaedfd528bfcc1f580/regex-2026.2.19-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52136f5b71f095cb74b736cc3a1b578030dada2e361ef2f07ca582240b703946", size = 781759 }, - { url = "https://files.pythonhosted.org/packages/37/45/9608ab1b41f6740ff4076eabadde8e8b3f3400942b348ac41e8599ccc131/regex-2026.2.19-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4192464fe3e6cb0ef6751f7d3b16f886d8270d359ed1590dd555539d364f0ff7", size = 850947 }, - { url = "https://files.pythonhosted.org/packages/90/3a/66471b6c4f7cac17e14bf5300e46661bba2b17ffb0871bd2759e837a6f82/regex-2026.2.19-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e561dd47a85d2660d3d3af4e6cb2da825cf20f121e577147963f875b83d32786", size = 898794 }, - { url = "https://files.pythonhosted.org/packages/c2/d2/38c53929a5931f7398e5e49f5a5a3079cb2aba30119b4350608364cfad8c/regex-2026.2.19-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00ec994d7824bf01cd6c7d14c7a6a04d9aeaf7c42a2bc22d2359d715634d539b", size = 791922 }, - { url = "https://files.pythonhosted.org/packages/8b/bd/b046e065630fa25059d9c195b7b5308ea94da45eee65d40879772500f74c/regex-2026.2.19-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2cb00aabd96b345d56a8c2bc328c8d6c4d29935061e05078bf1f02302e12abf5", size = 783345 }, - { url = "https://files.pythonhosted.org/packages/d4/8f/045c643d2fa255a985e8f87d848e4be230b711a8935e4bdc58e60b8f7b84/regex-2026.2.19-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f374366ed35673ea81b86a8859c457d4fae6ba092b71024857e9e237410c7404", size = 768055 }, - { url = "https://files.pythonhosted.org/packages/72/9f/ab7ae9f5447559562f1a788bbc85c0e526528c5e6c20542d18e4afc86aad/regex-2026.2.19-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f9417fd853fcd00b7d55167e692966dd12d95ba1a88bf08a62002ccd85030790", size = 774955 }, - { url = "https://files.pythonhosted.org/packages/37/5c/f16fc23c56f60b6f4ff194604a6e53bb8aec7b6e8e4a23a482dee8d77235/regex-2026.2.19-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:12e86a01594031abf892686fcb309b041bf3de3d13d99eb7e2b02a8f3c687df1", size = 846010 }, - { url = "https://files.pythonhosted.org/packages/51/c8/6be4c854135d7c9f35d4deeafdaf124b039ecb4ffcaeb7ed0495ad2c97ca/regex-2026.2.19-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:79014115e6fdf18fd9b32e291d58181bf42d4298642beaa13fd73e69810e4cb6", size = 755938 }, - { url = "https://files.pythonhosted.org/packages/d6/8d/f683d49b9663a5324b95a328e69d397f6dade7cb84154eec116bf79fe150/regex-2026.2.19-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:31aefac2506967b7dd69af2c58eca3cc8b086d4110b66d6ac6e9026f0ee5b697", size = 835773 }, - { url = "https://files.pythonhosted.org/packages/16/cd/619224b90da09f167fe4497c350a0d0b30edc539ee9244bf93e604c073c3/regex-2026.2.19-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:49cef7bb2a491f91a8869c7cdd90babf0a417047ab0bf923cd038ed2eab2ccb8", size = 780075 }, - { url = "https://files.pythonhosted.org/packages/5b/88/19cfb0c262d6f9d722edef29157125418bf90eb3508186bf79335afeedae/regex-2026.2.19-cp310-cp310-win32.whl", hash = "sha256:3a039474986e7a314ace6efb9ce52f5da2bdb80ac4955358723d350ec85c32ad", size = 266004 }, - { url = "https://files.pythonhosted.org/packages/82/af/5b487e0287ef72545d7ae92edecdacbe3d44e531cac24fda7de5598ba8dd/regex-2026.2.19-cp310-cp310-win_amd64.whl", hash = "sha256:5b81ff4f9cad99f90c807a00c5882fbcda86d8b3edd94e709fb531fc52cb3d25", size = 277895 }, - { url = "https://files.pythonhosted.org/packages/4c/19/b6715a187ffca4d2979af92a46ce922445ba41f910bf187ccd666a2d52ef/regex-2026.2.19-cp310-cp310-win_arm64.whl", hash = "sha256:a032bc01a4bc73fc3cadba793fce28eb420da39338f47910c59ffcc11a5ba5ef", size = 270465 }, +version = "2026.2.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/b8/845a927e078f5e5cc55d29f57becbfde0003d52806544531ab3f2da4503c/regex-2026.2.28-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fc48c500838be6882b32748f60a15229d2dea96e59ef341eaa96ec83538f498d", size = 488461, upload-time = "2026-02-28T02:15:48.405Z" }, + { url = "https://files.pythonhosted.org/packages/32/f9/8a0034716684e38a729210ded6222249f29978b24b684f448162ef21f204/regex-2026.2.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2afa673660928d0b63d84353c6c08a8a476ddfc4a47e11742949d182e6863ce8", size = 290774, upload-time = "2026-02-28T02:15:51.738Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ba/b27feefffbb199528dd32667cd172ed484d9c197618c575f01217fbe6103/regex-2026.2.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7ab218076eb0944549e7fe74cf0e2b83a82edb27e81cc87411f76240865e04d5", size = 288737, upload-time = "2026-02-28T02:15:53.534Z" }, + { url = "https://files.pythonhosted.org/packages/18/c5/65379448ca3cbfe774fcc33774dc8295b1ee97dc3237ae3d3c7b27423c9d/regex-2026.2.28-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d63db12e45a9b9f064bfe4800cefefc7e5f182052e4c1b774d46a40ab1d9bb", size = 782675, upload-time = "2026-02-28T02:15:55.488Z" }, + { url = "https://files.pythonhosted.org/packages/aa/30/6fa55bef48090f900fbd4649333791fc3e6467380b9e775e741beeb3231f/regex-2026.2.28-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:195237dc327858a7721bf8b0bbbef797554bc13563c3591e91cd0767bacbe359", size = 850514, upload-time = "2026-02-28T02:15:57.509Z" }, + { url = "https://files.pythonhosted.org/packages/a9/28/9ca180fb3787a54150209754ac06a42409913571fa94994f340b3bba4e1e/regex-2026.2.28-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b387a0d092dac157fb026d737dde35ff3e49ef27f285343e7c6401851239df27", size = 896612, upload-time = "2026-02-28T02:15:59.682Z" }, + { url = "https://files.pythonhosted.org/packages/46/b5/f30d7d3936d6deecc3ea7bea4f7d3c5ee5124e7c8de372226e436b330a55/regex-2026.2.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3935174fa4d9f70525a4367aaff3cb8bc0548129d114260c29d9dfa4a5b41692", size = 791691, upload-time = "2026-02-28T02:16:01.752Z" }, + { url = "https://files.pythonhosted.org/packages/f5/34/96631bcf446a56ba0b2a7f684358a76855dfe315b7c2f89b35388494ede0/regex-2026.2.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b2b23587b26496ff5fd40df4278becdf386813ec00dc3533fa43a4cf0e2ad3c", size = 783111, upload-time = "2026-02-28T02:16:03.651Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/f95cb7a85fe284d41cd2f3625e0f2ae30172b55dfd2af1d9b4eaef6259d7/regex-2026.2.28-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3b24bd7e9d85dc7c6a8bd2aa14ecd234274a0248335a02adeb25448aecdd420d", size = 767512, upload-time = "2026-02-28T02:16:05.616Z" }, + { url = "https://files.pythonhosted.org/packages/3d/af/a650f64a79c02a97f73f64d4e7fc4cc1984e64affab14075e7c1f9a2db34/regex-2026.2.28-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd477d5f79920338107f04aa645f094032d9e3030cc55be581df3d1ef61aa318", size = 773920, upload-time = "2026-02-28T02:16:08.325Z" }, + { url = "https://files.pythonhosted.org/packages/72/f8/3f9c2c2af37aedb3f5a1e7227f81bea065028785260d9cacc488e43e6997/regex-2026.2.28-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b49eb78048c6354f49e91e4b77da21257fecb92256b6d599ae44403cab30b05b", size = 846681, upload-time = "2026-02-28T02:16:10.381Z" }, + { url = "https://files.pythonhosted.org/packages/54/12/8db04a334571359f4d127d8f89550917ec6561a2fddfd69cd91402b47482/regex-2026.2.28-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a25c7701e4f7a70021db9aaf4a4a0a67033c6318752146e03d1b94d32006217e", size = 755565, upload-time = "2026-02-28T02:16:11.972Z" }, + { url = "https://files.pythonhosted.org/packages/da/bc/91c22f384d79324121b134c267a86ca90d11f8016aafb1dc5bee05890ee3/regex-2026.2.28-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9dd450db6458387167e033cfa80887a34c99c81d26da1bf8b0b41bf8c9cac88e", size = 835789, upload-time = "2026-02-28T02:16:14.036Z" }, + { url = "https://files.pythonhosted.org/packages/46/a7/4cc94fd3af01dcfdf5a9ed75c8e15fd80fcd62cc46da7592b1749e9c35db/regex-2026.2.28-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2954379dd20752e82d22accf3ff465311cbb2bac6c1f92c4afd400e1757f7451", size = 780094, upload-time = "2026-02-28T02:16:15.468Z" }, + { url = "https://files.pythonhosted.org/packages/3c/21/e5a38f420af3c77cab4a65f0c3a55ec02ac9babf04479cfd282d356988a6/regex-2026.2.28-cp310-cp310-win32.whl", hash = "sha256:1f8b17be5c27a684ea6759983c13506bd77bfc7c0347dff41b18ce5ddd2ee09a", size = 266025, upload-time = "2026-02-28T02:16:16.828Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0a/205c4c1466a36e04d90afcd01d8908bac327673050c7fe316b2416d99d3d/regex-2026.2.28-cp310-cp310-win_amd64.whl", hash = "sha256:dd8847c4978bc3c7e6c826fb745f5570e518b8459ac2892151ce6627c7bc00d5", size = 277965, upload-time = "2026-02-28T02:16:18.752Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4d/29b58172f954b6ec2c5ed28529a65e9026ab96b4b7016bcd3858f1c31d3c/regex-2026.2.28-cp310-cp310-win_arm64.whl", hash = "sha256:73cdcdbba8028167ea81490c7f45280113e41db2c7afb65a276f4711fa3bcbff", size = 270336, upload-time = "2026-02-28T02:16:20.735Z" }, ] [[package]] @@ -2744,9 +2810,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [package.optional-dependencies] @@ -2761,18 +2827,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, ] [[package]] name = "rfc3986-validator" version = "0.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, ] [[package]] @@ -2782,9 +2848,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lark" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, ] [[package]] @@ -2795,79 +2861,79 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458 }, + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, ] [[package]] name = "rich-toolkit" -version = "0.19.4" +version = "0.19.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/c9/4bbf4bfee195ed1b7d7a6733cc523ca61dbfb4a3e3c12ea090aaffd97597/rich_toolkit-0.19.4.tar.gz", hash = "sha256:52e23d56f9dc30d1343eb3b3f6f18764c313fbfea24e52e6a1d6069bec9c18eb", size = 193951 } +sdist = { url = "https://files.pythonhosted.org/packages/42/ba/dae9e3096651042754da419a4042bc1c75e07d615f9b15066d738838e4df/rich_toolkit-0.19.7.tar.gz", hash = "sha256:133c0915872da91d4c25d85342d5ec1dfacc69b63448af1a08a0d4b4f23ef46e", size = 195877, upload-time = "2026-02-24T16:06:20.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/31/97d39719def09c134385bfcfbedfed255168b571e7beb3ad7765aae660ca/rich_toolkit-0.19.4-py3-none-any.whl", hash = "sha256:34ac344de8862801644be8b703e26becf44b047e687f208d7829e8f7cfc311d6", size = 32757 }, + { url = "https://files.pythonhosted.org/packages/fb/3c/c923619f6d2f5fafcc96fec0aaf9550a46cd5b6481f06e0c6b66a2a4fed0/rich_toolkit-0.19.7-py3-none-any.whl", hash = "sha256:0288e9203728c47c5a4eb60fd2f0692d9df7455a65901ab6f898437a2ba5989d", size = 32963, upload-time = "2026-02-24T16:06:22.066Z" }, ] [[package]] name = "rignore" version = "0.7.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999 }, - { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824 }, - { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121 }, - { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813 }, - { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019 }, - { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822 }, - { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820 }, - { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050 }, - { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164 }, - { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028 }, - { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024 }, - { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531 }, - { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817 }, - { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001 }, - { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462 }, - { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918 }, - { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922 }, - { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987 }, - { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110 }, - { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339 }, - { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680 }, - { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045 }, - { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310 }, - { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998 }, - { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178 }, - { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190 }, +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999, upload-time = "2025-11-05T20:42:38.373Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824, upload-time = "2025-11-05T20:42:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121, upload-time = "2025-11-05T20:40:48.94Z" }, + { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813, upload-time = "2025-11-05T20:41:04.71Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019, upload-time = "2025-11-05T20:41:20.723Z" }, + { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822, upload-time = "2025-11-05T20:41:36.99Z" }, + { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820, upload-time = "2025-11-05T20:42:06.364Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050, upload-time = "2025-11-05T20:41:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164, upload-time = "2025-11-05T21:40:10.368Z" }, + { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028, upload-time = "2025-11-05T21:40:27.977Z" }, + { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024, upload-time = "2025-11-05T21:40:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531, upload-time = "2025-11-05T21:41:02.734Z" }, + { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817, upload-time = "2025-11-05T21:41:47.51Z" }, + { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001, upload-time = "2025-11-05T21:41:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462, upload-time = "2025-11-05T20:42:50.804Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918, upload-time = "2025-11-05T20:42:33.689Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922, upload-time = "2025-11-05T20:41:00.361Z" }, + { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987, upload-time = "2025-11-05T20:41:16.219Z" }, + { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110, upload-time = "2025-11-05T20:41:32.631Z" }, + { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339, upload-time = "2025-11-05T20:41:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680, upload-time = "2025-11-05T20:42:18.061Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045, upload-time = "2025-11-05T20:42:02.315Z" }, + { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310, upload-time = "2025-11-05T21:40:23.184Z" }, + { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998, upload-time = "2025-11-05T21:40:40.603Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178, upload-time = "2025-11-05T21:40:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190, upload-time = "2025-11-05T21:41:16.403Z" }, ] [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490 }, - { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751 }, - { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696 }, - { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136 }, - { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699 }, - { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022 }, - { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522 }, - { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579 }, - { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305 }, - { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503 }, - { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322 }, - { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792 }, - { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901 }, - { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823 }, + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, ] [[package]] @@ -2877,35 +2943,35 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827 } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830 }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, ] [[package]] name = "safetensors" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, - { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430 }, - { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977 }, - { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890 }, - { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885 }, +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, ] [[package]] @@ -2918,13 +2984,13 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136 } +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221 }, - { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834 }, - { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938 }, - { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818 }, - { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969 }, + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, ] [[package]] @@ -2934,16 +3000,16 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/e5/0230da034a2e1b1feb32621d7cd57c59484091d6dccc9e6b855b0d309fc9/scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b", size = 58618870, upload-time = "2024-06-24T20:35:18.532Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226 }, - { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893 }, - { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258 }, - { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715 }, - { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038 }, - { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959 }, - { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514 }, - { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252 }, + { url = "https://files.pythonhosted.org/packages/c6/90/face72921ce52d74880b380e6f86b3caa6c65766c5808fbe179e208b9c6d/scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484", size = 39120226, upload-time = "2024-06-24T20:31:50.451Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a1/0093566d31ae662e942d4079e2a4dea4256723bf3d072ae67f5ba41aee0d/scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6", size = 29866893, upload-time = "2024-06-24T20:31:57.337Z" }, + { url = "https://files.pythonhosted.org/packages/52/21/05a182fb405a53dfbdf6415308bf185677e89188bc2206de011a3653f48e/scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7", size = 23076258, upload-time = "2024-06-24T20:32:02.711Z" }, + { url = "https://files.pythonhosted.org/packages/5c/63/9954d14012a2f4aff4570f1aaf076d7f65f3fc246ae4483b765488d57d51/scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1", size = 25454715, upload-time = "2024-06-24T20:32:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/57/b8/ca969a99d34956c6546cbb9ea3f863a387009f68cdbad13cdb07db0cc23d/scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0", size = 35569038, upload-time = "2024-06-24T20:32:17.305Z" }, + { url = "https://files.pythonhosted.org/packages/e2/20/15c8fe0dfebb6facd81b3d08bf45dfa080e305deb17172b0a40eba59e927/scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0", size = 41135959, upload-time = "2024-06-24T20:32:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/8721f93fbf98a69067d20bdfded36a7de2a3d811f192edba9eeefbde61b8/scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d", size = 41118514, upload-time = "2024-06-24T20:32:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0c/82c1330c08f31d61142d38cb9a185e01c2403c990d10dab208032e62d0fa/scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359", size = 44763252, upload-time = "2024-06-24T20:32:45.06Z" }, ] [[package]] @@ -2955,9 +3021,9 @@ dependencies = [ { name = "numpy" }, { name = "pandas" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, ] [[package]] @@ -2967,23 +3033,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "regex" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/8a/1ae8e3deb805831933b63d58851ab41ff2472099e15511fc62039421ad70/segtok-1.5.11.tar.gz", hash = "sha256:8ab2dd44245bcbfec25b575dc4618473bbdf2af8c2649698cd5a370f42f3db23", size = 25244, upload-time = "2021-12-15T21:56:14.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/dd/60/d384dbae5d4756e33f1750fa3472303de2c827011907a64e213e114d0556/segtok-1.5.11-py3-none-any.whl", hash = "sha256:910616b76198c3141b2772df530270d3b706e42ae69a5b30ef115c7bd5d1501a", size = 24332, upload-time = "2021-12-15T21:56:12.508Z" }, ] [[package]] name = "send2trash" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255 } +sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255, upload-time = "2026-01-14T06:27:36.056Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610 }, + { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610, upload-time = "2026-01-14T06:27:35.218Z" }, ] [[package]] name = "sentence-transformers" -version = "5.2.3" +version = "5.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -2995,110 +3061,110 @@ dependencies = [ { name = "transformers" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/30/21664028fc0776eb1ca024879480bbbab36f02923a8ff9e4cae5a150fa35/sentence_transformers-5.2.3.tar.gz", hash = "sha256:3cd3044e1f3fe859b6a1b66336aac502eaae5d3dd7d5c8fc237f37fbf58137c7", size = 381623 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/26/448453925b6ce0c29d8b54327caa71ee4835511aef02070467402273079c/sentence_transformers-5.3.0.tar.gz", hash = "sha256:414a0a881f53a4df0e6cbace75f823bfcb6b94d674c42a384b498959b7c065e2", size = 403330, upload-time = "2026-03-12T14:53:40.778Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/9f/dba4b3e18ebbe1eaa29d9f1764fbc7da0cd91937b83f2b7928d15c5d2d36/sentence_transformers-5.2.3-py3-none-any.whl", hash = "sha256:6437c62d4112b615ddebda362dfc16a4308d604c5b68125ed586e3e95d5b2e30", size = 494225 }, + { url = "https://files.pythonhosted.org/packages/e2/9c/2fa7224058cad8df68d84bafee21716f30892cecc7ad1ad73bde61d23754/sentence_transformers-5.3.0-py3-none-any.whl", hash = "sha256:dca6b98db790274a68185d27a65801b58b4caf653a4e556b5f62827509347c7d", size = 512390, upload-time = "2026-03-12T14:53:39.035Z" }, ] [[package]] name = "sentencepiece" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/d2/b9c7ca067c26d8ff085d252c89b5f69609ca93fb85a00ede95f4857865d4/sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843", size = 2632106, upload-time = "2024-02-19T17:06:47.428Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979 }, - { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845 }, - { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472 }, - { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151 }, - { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931 }, - { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537 }, - { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747 }, - { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525 }, + { url = "https://files.pythonhosted.org/packages/f6/71/98648c3b64b23edb5403f74bcc906ad21766872a6e1ada26ea3f1eb941ab/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227", size = 2408979, upload-time = "2024-02-19T17:05:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/77/9f/7efbaa6d4c0c718a9affbecc536b03ca62f99f421bdffb531c16030e2d2b/sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452", size = 1238845, upload-time = "2024-02-19T17:05:37.371Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e4/c2541027a43ec6962ba9b601805d17ba3f86b38bdeae0e8ac65a2981e248/sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3", size = 1181472, upload-time = "2024-02-19T17:05:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/fd/46/316c1ba6c52b97de76aff7b9da678f7afbb52136afb2987c474d95630e65/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a", size = 1259151, upload-time = "2024-02-19T17:05:42.594Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5a/3c48738a0835d76dd06c62b6ac48d39c923cde78dd0f587353bdcbb99851/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e", size = 1355931, upload-time = "2024-02-19T17:05:44.695Z" }, + { url = "https://files.pythonhosted.org/packages/a6/27/33019685023221ca8ed98e8ceb7ae5e166032686fa3662c68f1f1edf334e/sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040", size = 1301537, upload-time = "2024-02-19T17:05:46.713Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e4/55f97cef14293171fef5f96e96999919ab5b4d1ce95b53547ad653d7e3bf/sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d", size = 936747, upload-time = "2024-02-19T17:05:48.705Z" }, + { url = "https://files.pythonhosted.org/packages/85/f4/4ef1a6e0e9dbd8a60780a91df8b7452ada14cfaa0e17b3b8dfa42cecae18/sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2", size = 991525, upload-time = "2024-02-19T17:05:55.145Z" }, ] [[package]] name = "sentry-sdk" -version = "2.53.0" +version = "2.54.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/06/66c8b705179bc54087845f28fd1b72f83751b6e9a195628e2e9af9926505/sentry_sdk-2.53.0.tar.gz", hash = "sha256:6520ef2c4acd823f28efc55e43eb6ce2e6d9f954a95a3aa96b6fd14871e92b77", size = 412369 } +sdist = { url = "https://files.pythonhosted.org/packages/c8/e9/2e3a46c304e7fa21eaa70612f60354e32699c7102eb961f67448e222ad7c/sentry_sdk-2.54.0.tar.gz", hash = "sha256:2620c2575128d009b11b20f7feb81e4e4e8ae08ec1d36cbc845705060b45cc1b", size = 413813, upload-time = "2026-03-02T15:12:41.355Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/d4/2fdf854bc3b9c7f55219678f812600a20a138af2dd847d99004994eada8f/sentry_sdk-2.53.0-py2.py3-none-any.whl", hash = "sha256:46e1ed8d84355ae54406c924f6b290c3d61f4048625989a723fd622aab838899", size = 437908 }, + { url = "https://files.pythonhosted.org/packages/53/39/be412cc86bc6247b8f69e9383d7950711bd86f8d0a4a4b0fe8fad685bc21/sentry_sdk-2.54.0-py2.py3-none-any.whl", hash = "sha256:fd74e0e281dcda63afff095d23ebcd6e97006102cdc8e78a29f19ecdf796a0de", size = 439198, upload-time = "2026-03-02T15:12:39.546Z" }, ] [[package]] name = "setuptools" -version = "82.0.0" +version = "82.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/f3/748f4d6f65d1756b9ae577f329c951cda23fb900e4de9f70900ced962085/setuptools-82.0.0.tar.gz", hash = "sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb", size = 1144893 } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/c6/76dc613121b793286a3f91621d7b75a2b493e0390ddca50f11993eadf192/setuptools-82.0.0-py3-none-any.whl", hash = "sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0", size = 1003468 }, + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, ] [[package]] name = "soupsieve" version = "2.8.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627 } +sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016 }, + { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, ] [[package]] name = "sqlalchemy" -version = "2.0.46" +version = "2.0.48" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/aa/9ce0f3e7a9829ead5c8ce549392f33a12c4555a6c0609bb27d882e9c7ddf/sqlalchemy-2.0.46.tar.gz", hash = "sha256:cf36851ee7219c170bb0793dbc3da3e80c582e04a5437bc601bfe8c85c9216d7", size = 9865393 } +sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/26/66ba59328dc25e523bfcb0f8db48bdebe2035e0159d600e1f01c0fc93967/sqlalchemy-2.0.46-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:895296687ad06dc9b11a024cf68e8d9d3943aa0b4964278d2553b86f1b267735", size = 2155051 }, - { url = "https://files.pythonhosted.org/packages/21/cd/9336732941df972fbbfa394db9caa8bb0cf9fe03656ec728d12e9cbd6edc/sqlalchemy-2.0.46-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab65cb2885a9f80f979b85aa4e9c9165a31381ca322cbde7c638fe6eefd1ec39", size = 3234666 }, - { url = "https://files.pythonhosted.org/packages/38/62/865ae8b739930ec433cd4123760bee7f8dafdc10abefd725a025604fb0de/sqlalchemy-2.0.46-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52fe29b3817bd191cc20bad564237c808967972c97fa683c04b28ec8979ae36f", size = 3232917 }, - { url = "https://files.pythonhosted.org/packages/24/38/805904b911857f2b5e00fdea44e9570df62110f834378706939825579296/sqlalchemy-2.0.46-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:09168817d6c19954d3b7655da6ba87fcb3a62bb575fb396a81a8b6a9fadfe8b5", size = 3185790 }, - { url = "https://files.pythonhosted.org/packages/69/4f/3260bb53aabd2d274856337456ea52f6a7eccf6cce208e558f870cec766b/sqlalchemy-2.0.46-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:be6c0466b4c25b44c5d82b0426b5501de3c424d7a3220e86cd32f319ba56798e", size = 3207206 }, - { url = "https://files.pythonhosted.org/packages/ce/b3/67c432d7f9d88bb1a61909b67e29f6354d59186c168fb5d381cf438d3b73/sqlalchemy-2.0.46-cp310-cp310-win32.whl", hash = "sha256:1bc3f601f0a818d27bfe139f6766487d9c88502062a2cd3a7ee6c342e81d5047", size = 2115296 }, - { url = "https://files.pythonhosted.org/packages/4a/8c/25fb284f570f9d48e6c240f0269a50cec9cf009a7e08be4c0aaaf0654972/sqlalchemy-2.0.46-cp310-cp310-win_amd64.whl", hash = "sha256:e0c05aff5c6b1bb5fb46a87e0f9d2f733f83ef6cbbbcd5c642b6c01678268061", size = 2138540 }, - { url = "https://files.pythonhosted.org/packages/fc/a1/9c4efa03300926601c19c18582531b45aededfb961ab3c3585f1e24f120b/sqlalchemy-2.0.46-py3-none-any.whl", hash = "sha256:f9c11766e7e7c0a2767dda5acb006a118640c9fc0a4104214b96269bfb78399e", size = 1937882 }, + { url = "https://files.pythonhosted.org/packages/9a/67/1235676e93dd3b742a4a8eddfae49eea46c85e3eed29f0da446a8dd57500/sqlalchemy-2.0.48-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89", size = 2157384, upload-time = "2026-03-02T15:38:26.781Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d7/fa728b856daa18c10e1390e76f26f64ac890c947008284387451d56ca3d0/sqlalchemy-2.0.48-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0", size = 3236981, upload-time = "2026-03-02T15:58:53.53Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ad/6c4395649a212a6c603a72c5b9ab5dce3135a1546cfdffa3c427e71fd535/sqlalchemy-2.0.48-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd", size = 3235232, upload-time = "2026-03-02T15:52:25.654Z" }, + { url = "https://files.pythonhosted.org/packages/01/f4/58f845e511ac0509765a6f85eb24924c1ef0d54fb50de9d15b28c3601458/sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29", size = 3188106, upload-time = "2026-03-02T15:58:55.193Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f9/6dcc7bfa5f5794c3a095e78cd1de8269dfb5584dfd4c2c00a50d3c1ade44/sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0", size = 3209522, upload-time = "2026-03-02T15:52:27.407Z" }, + { url = "https://files.pythonhosted.org/packages/d7/5a/b632875ab35874d42657f079529f0745410604645c269a8c21fb4272ff7a/sqlalchemy-2.0.48-cp310-cp310-win32.whl", hash = "sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018", size = 2117695, upload-time = "2026-03-02T15:46:51.389Z" }, + { url = "https://files.pythonhosted.org/packages/de/03/9752eb2a41afdd8568e41ac3c3128e32a0a73eada5ab80483083604a56d1/sqlalchemy-2.0.48-cp310-cp310-win_amd64.whl", hash = "sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76", size = 2140928, upload-time = "2026-03-02T15:46:52.992Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, ] [[package]] name = "sqlitedict" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846 } +sdist = { url = "https://files.pythonhosted.org/packages/12/9a/7620d1e9dcb02839ed6d4b14064e609cdd7a8ae1e47289aa0456796dd9ca/sqlitedict-2.1.0.tar.gz", hash = "sha256:03d9cfb96d602996f1d4c2db2856f1224b96a9c431bdd16e78032a72940f9e8c", size = 21846, upload-time = "2022-12-03T13:39:13.102Z" } [[package]] name = "sqlmodel" @@ -3108,9 +3174,9 @@ dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392, upload-time = "2024-08-31T09:43:24.088Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, + { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276, upload-time = "2024-08-31T09:43:22.358Z" }, ] [[package]] @@ -3122,9 +3188,9 @@ dependencies = [ { name = "executing" }, { name = "pure-eval" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] [[package]] @@ -3135,9 +3201,9 @@ dependencies = [ { name = "anyio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272 }, + { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, ] [[package]] @@ -3147,50 +3213,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] [[package]] name = "tabulate" -version = "0.9.0" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" }, ] [[package]] name = "tenacity" version = "9.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413 } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926 }, -] - -[[package]] -name = "tensorboardx" -version = "2.6.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "packaging" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2b/c5/d4cc6e293fb837aaf9f76dd7745476aeba8ef7ef5146c3b3f9ee375fe7a5/tensorboardx-2.6.4.tar.gz", hash = "sha256:b163ccb7798b31100b9f5fa4d6bc22dad362d7065c2f24b51e50731adde86828", size = 4769801 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/1d/b5d63f1a6b824282b57f7b581810d20b7a28ca951f2d5b59f1eb0782c12b/tensorboardx-2.6.4-py3-none-any.whl", hash = "sha256:5970cf3a1f0a6a6e8b180ccf46f3fe832b8a25a70b86e5a237048a7c0beb18e2", size = 87201 }, -] - -[[package]] -name = "termcolor" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734 }, + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, ] [[package]] @@ -3202,18 +3245,18 @@ dependencies = [ { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, ] [[package]] name = "threadpoolctl" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] [[package]] @@ -3223,9 +3266,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, ] [[package]] @@ -3235,36 +3278,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, - { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301 }, - { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308 }, - { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964 }, - { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542 }, +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, ] [[package]] name = "tomli" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477 } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477 }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, ] [[package]] @@ -3272,69 +3315,54 @@ name = "torch" version = "2.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457 }, - { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962 }, - { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237 }, - { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931 }, - { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450 }, -] - -[[package]] -name = "torchmetrics" -version = "0.11.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "packaging" }, - { name = "torch" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/ed/9f76e2d65d2e6d67a0ba097f34f4b618fb7466d731476d3d3440dfe2cb8e/torchmetrics-0.11.4.tar.gz", hash = "sha256:1fe45a14b44dd65d90199017dd5a4b5a128d56a8a311da7916c402c18c671494", size = 307144 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/47/6e9f9b41c48750a45ad07cc6d43a2979bfc09e6989656aece97cc59cbef1/torchmetrics-0.11.4-py3-none-any.whl", hash = "sha256:45f892f3534e91f3ad9e2488d1b05a93b7cb76b7d037969435a41a1f24750d9a", size = 519162 }, + { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457, upload-time = "2026-02-10T21:44:59.189Z" }, + { url = "https://files.pythonhosted.org/packages/16/ee/efbd56687be60ef9af0c9c0ebe106964c07400eade5b0af8902a1d8cd58c/torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321", size = 915510070, upload-time = "2026-03-11T14:16:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" }, + { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" }, + { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450, upload-time = "2026-01-21T16:25:30.692Z" }, ] [[package]] name = "tornado" -version = "6.5.4" +version = "6.5.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909 }, - { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163 }, - { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746 }, - { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083 }, - { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315 }, - { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003 }, - { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412 }, - { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392 }, - { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481 }, - { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886 }, - { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910 }, + { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" }, + { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" }, + { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" }, + { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" }, + { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" }, ] [[package]] @@ -3342,20 +3370,20 @@ name = "tqdm" version = "4.67.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598 } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374 }, + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] [[package]] name = "traitlets" version = "5.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, ] [[package]] @@ -3366,9 +3394,9 @@ dependencies = [ { name = "torch" }, { name = "transformers", extra = ["sentencepiece", "torch"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754 } +sdist = { url = "https://files.pythonhosted.org/packages/45/6f/85142d145fd2c453053e7dcd5500c31cd26ce51f9010cf0fe698001853f2/transformer_smaller_training_vocab-0.4.2.tar.gz", hash = "sha256:aed4390331e63d9a0998d76f277b7d44ad5d38b8427ad7792b1e28c807801ed8", size = 11754, upload-time = "2025-06-15T13:34:38.522Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/6c/88/94fa030995bc9a54e911172fd6ca26a81c2a5ddafd896ff62ad9cc99088b/transformer_smaller_training_vocab-0.4.2-py3-none-any.whl", hash = "sha256:49fcdb3134ede5faca41d3bed2588bd21a4098b64f261e7b198f163d394c3ef0", size = 14073, upload-time = "2025-06-15T13:34:37.468Z" }, ] [[package]] @@ -3387,9 +3415,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912, upload-time = "2026-01-16T10:38:39.284Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498 }, + { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498, upload-time = "2026-01-16T10:38:31.289Z" }, ] [package.optional-dependencies] @@ -3407,8 +3435,7 @@ name = "triton" version = "3.6.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/ba/b1b04f4b291a3205d95ebd24465de0e5bf010a2df27a4e58a9b5f039d8f2/triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781", size = 175972180 }, - { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201 }, + { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, ] [[package]] @@ -3421,18 +3448,18 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085 }, + { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, ] [[package]] name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] @@ -3442,45 +3469,45 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] name = "tzdata" version = "2025.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521 }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] [[package]] name = "unidecode" version = "1.3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701 } +sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701, upload-time = "2024-01-11T11:58:35.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494 }, + { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494, upload-time = "2024-01-11T11:58:33.012Z" }, ] [[package]] name = "uri-template" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, ] [[package]] name = "urllib3" version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584 }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] @@ -3492,9 +3519,9 @@ dependencies = [ { name = "h11" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/ce/eeb58ae4ac36fe09e3842eb02e0eb676bf2c53ae062b98f1b2531673efdd/uvicorn-0.41.0.tar.gz", hash = "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", size = 82633 } +sdist = { url = "https://files.pythonhosted.org/packages/32/ce/eeb58ae4ac36fe09e3842eb02e0eb676bf2c53ae062b98f1b2531673efdd/uvicorn-0.41.0.tar.gz", hash = "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", size = 82633, upload-time = "2026-02-16T23:07:24.1Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783 }, + { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, ] [package.optional-dependencies] @@ -3512,29 +3539,30 @@ standard = [ name = "uvloop" version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335 }, - { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903 }, - { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499 }, - { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133 }, - { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681 }, - { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261 }, + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" }, ] [[package]] name = "virtualenv" -version = "20.39.0" +version = "21.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, + { name = "python-discovery" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/54/809199edc537dbace273495ac0884d13df26436e910a5ed4d0ec0a69806b/virtualenv-20.39.0.tar.gz", hash = "sha256:a15f0cebd00d50074fd336a169d53422436a12dfe15149efec7072cfe817df8b", size = 5869141 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/b4/8268da45f26f4fe84f6eae80a6ca1485ffb490a926afecff75fc48f61979/virtualenv-20.39.0-py3-none-any.whl", hash = "sha256:44888bba3775990a152ea1f73f8e5f566d49f11bbd1de61d426fd7732770043e", size = 5839121 }, + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, ] [[package]] @@ -3544,174 +3572,182 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318 }, - { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478 }, - { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894 }, - { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065 }, - { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377 }, - { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837 }, - { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456 }, - { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614 }, - { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690 }, - { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459 }, - { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663 }, - { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453 }, - { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611 }, - { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889 }, - { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616 }, - { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413 }, + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" }, ] [[package]] name = "wcwidth" version = "0.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684 } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189 }, + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, ] [[package]] name = "webcolors" version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, ] [[package]] name = "webencodings" version = "0.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, ] [[package]] name = "websocket-client" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, ] [[package]] name = "websockets" version = "16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346 } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343 }, - { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021 }, - { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320 }, - { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815 }, - { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054 }, - { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565 }, - { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848 }, - { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249 }, - { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685 }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598 }, + { url = "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343, upload-time = "2026-01-10T09:22:21.28Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021, upload-time = "2026-01-10T09:22:22.696Z" }, + { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320, upload-time = "2026-01-10T09:22:23.94Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815, upload-time = "2026-01-10T09:22:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054, upload-time = "2026-01-10T09:22:27.101Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565, upload-time = "2026-01-10T09:22:28.293Z" }, + { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848, upload-time = "2026-01-10T09:22:30.394Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249, upload-time = "2026-01-10T09:22:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685, upload-time = "2026-01-10T09:22:33.345Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] [[package]] name = "widgetsnbextension" version = "4.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, ] [[package]] name = "wikipedia-api" -version = "0.9.0" +version = "0.10.2" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "click" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/7b/8b775cf22f266086cefa762b58df0a35a4c4532de3f2f6a0b7432fd31fd1/wikipedia_api-0.9.0.tar.gz", hash = "sha256:6aabd1f9fe34f594f4d36f5af67e468b0a50d713aa9a1d102b4d996bc76dd58e", size = 20041 } +sdist = { url = "https://files.pythonhosted.org/packages/cb/59/a6f5790043cff046658d44bc5b594a7d0cc0d9a1a6911a0df6e7aba2179c/wikipedia_api-0.10.2.tar.gz", hash = "sha256:93fc84d2d88b043c626a03bc013a741c206ab60c0517bfce51fa60a0edc5087d", size = 29841, upload-time = "2026-03-06T22:01:16.615Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/41/cf1ceb3071b58175d6960d5f45bfd5c007fc8ab5bec8ca189efd02bb05e4/wikipedia_api-0.10.2-py3-none-any.whl", hash = "sha256:0aa6d09e46909d396d81af97de5dee06004c1f823725b4f66c352b1096d48163", size = 22690, upload-time = "2026-03-06T22:01:15.024Z" }, +] [[package]] name = "wrapt" -version = "2.1.1" +version = "2.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/37/ae31f40bec90de2f88d9597d0b5281e23ffe85b893a47ca5d9c05c63a4f6/wrapt-2.1.1.tar.gz", hash = "sha256:5fdcb09bf6db023d88f312bd0767594b414655d58090fc1c46b3414415f67fac", size = 81329 } +sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/21/293b657a27accfbbbb6007ebd78af0efa2083dac83e8f523272ea09b4638/wrapt-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e927375e43fd5a985b27a8992327c22541b6dede1362fc79df337d26e23604f", size = 60554 }, - { url = "https://files.pythonhosted.org/packages/25/e9/96dd77728b54a899d4ce2798d7b1296989ce687ed3c0cb917d6b3154bf5d/wrapt-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c99544b6a7d40ca22195563b6d8bc3986ee8bb82f272f31f0670fe9440c869", size = 61496 }, - { url = "https://files.pythonhosted.org/packages/44/79/4c755b45df6ef30c0dd628ecfaa0c808854be147ca438429da70a162833c/wrapt-2.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b2be3fa5f4efaf16ee7c77d0556abca35f5a18ad4ac06f0ef3904c3399010ce9", size = 113528 }, - { url = "https://files.pythonhosted.org/packages/9f/63/23ce28f7b841217d9a6337a340fbb8d4a7fbd67a89d47f377c8550fa34aa/wrapt-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67c90c1ae6489a6cb1a82058902caa8006706f7b4e8ff766f943e9d2c8e608d0", size = 115536 }, - { url = "https://files.pythonhosted.org/packages/23/7b/5ca8d3b12768670d16c8329e29960eedd56212770365a02a8de8bf73dc01/wrapt-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05c0db35ccffd7480143e62df1e829d101c7b86944ae3be7e4869a7efa621f53", size = 114716 }, - { url = "https://files.pythonhosted.org/packages/c7/3a/9789ccb14a096d30bb847bf3ee137bf682cc9750c2ce155f4c5ae1962abf/wrapt-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0c2ec9f616755b2e1e0bf4d0961f59bb5c2e7a77407e7e2c38ef4f7d2fdde12c", size = 113200 }, - { url = "https://files.pythonhosted.org/packages/cf/e5/4ec3526ce6ce920b267c8d35d2c2f0874d3fad2744c8b7259353f1132baa/wrapt-2.1.1-cp310-cp310-win32.whl", hash = "sha256:203ba6b3f89e410e27dbd30ff7dccaf54dcf30fda0b22aa1b82d560c7f9fe9a1", size = 57876 }, - { url = "https://files.pythonhosted.org/packages/d1/4e/661c7c76ecd85375b2bc03488941a3a1078642af481db24949e2b9de01f4/wrapt-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:6f9426d9cfc2f8732922fc96198052e55c09bb9db3ddaa4323a18e055807410e", size = 60224 }, - { url = "https://files.pythonhosted.org/packages/5f/b7/53c7252d371efada4cb119e72e774fa2c6b3011fc33e3e552cdf48fb9488/wrapt-2.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:69c26f51b67076b40714cff81bdd5826c0b10c077fb6b0678393a6a2f952a5fc", size = 58645 }, - { url = "https://files.pythonhosted.org/packages/c4/da/5a086bf4c22a41995312db104ec2ffeee2cf6accca9faaee5315c790377d/wrapt-2.1.1-py3-none-any.whl", hash = "sha256:3b0f4629eb954394a3d7c7a1c8cca25f0b07cefe6aa8545e862e9778152de5b7", size = 43886 }, + { url = "https://files.pythonhosted.org/packages/da/d2/387594fb592d027366645f3d7cc9b4d7ca7be93845fbaba6d835a912ef3c/wrapt-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c", size = 60669, upload-time = "2026-03-06T02:52:40.671Z" }, + { url = "https://files.pythonhosted.org/packages/c9/18/3f373935bc5509e7ac444c8026a56762e50c1183e7061797437ca96c12ce/wrapt-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f", size = 61603, upload-time = "2026-03-06T02:54:21.032Z" }, + { url = "https://files.pythonhosted.org/packages/c2/7a/32758ca2853b07a887a4574b74e28843919103194bb47001a304e24af62f/wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb", size = 113632, upload-time = "2026-03-06T02:53:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d5/eeaa38f670d462e97d978b3b0d9ce06d5b91e54bebac6fbed867809216e7/wrapt-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e", size = 115644, upload-time = "2026-03-06T02:54:53.33Z" }, + { url = "https://files.pythonhosted.org/packages/e3/09/2a41506cb17affb0bdf9d5e2129c8c19e192b388c4c01d05e1b14db23c00/wrapt-2.1.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba", size = 112016, upload-time = "2026-03-06T02:54:43.274Z" }, + { url = "https://files.pythonhosted.org/packages/64/15/0e6c3f5e87caadc43db279724ee36979246d5194fa32fed489c73643ba59/wrapt-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f", size = 114823, upload-time = "2026-03-06T02:54:29.392Z" }, + { url = "https://files.pythonhosted.org/packages/56/b2/0ad17c8248f4e57bedf44938c26ec3ee194715f812d2dbbd9d7ff4be6c06/wrapt-2.1.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394", size = 111244, upload-time = "2026-03-06T02:54:02.149Z" }, + { url = "https://files.pythonhosted.org/packages/ff/04/bcdba98c26f2c6522c7c09a726d5d9229120163493620205b2f76bd13c01/wrapt-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45", size = 113307, upload-time = "2026-03-06T02:54:12.428Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1b/5e2883c6bc14143924e465a6fc5a92d09eeabe35310842a481fb0581f832/wrapt-2.1.2-cp310-cp310-win32.whl", hash = "sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d", size = 57986, upload-time = "2026-03-06T02:54:26.823Z" }, + { url = "https://files.pythonhosted.org/packages/42/5a/4efc997bccadd3af5749c250b49412793bc41e13a83a486b2b54a33e240c/wrapt-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71", size = 60336, upload-time = "2026-03-06T02:54:18Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f5/a2bb833e20181b937e87c242645ed5d5aa9c373006b0467bfe1a35c727d0/wrapt-2.1.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc", size = 58757, upload-time = "2026-03-06T02:53:51.545Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, ] [[package]] name = "xmltodict" version = "0.14.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942 } +sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942, upload-time = "2024-10-16T06:10:29.683Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981 }, + { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981, upload-time = "2024-10-16T06:10:27.649Z" }, ] [[package]] name = "xxhash" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160 } +sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845 }, - { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807 }, - { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786 }, - { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830 }, - { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606 }, - { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872 }, - { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217 }, - { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139 }, - { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669 }, - { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018 }, - { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058 }, - { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628 }, - { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577 }, - { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487 }, - { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863 }, + { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" }, + { url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" }, + { url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" }, + { url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" }, + { url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" }, + { url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" }, + { url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" }, + { url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" }, ] [[package]] name = "yarl" -version = "1.22.0" +version = "1.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517 }, - { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495 }, - { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400 }, - { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545 }, - { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598 }, - { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893 }, - { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240 }, - { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965 }, - { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026 }, - { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637 }, - { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082 }, - { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811 }, - { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223 }, - { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118 }, - { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852 }, - { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012 }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814 }, +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" }, + { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" }, + { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" }, + { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" }, + { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" }, + { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" }, + { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" }, + { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" }, + { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" }, + { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" }, + { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, ] From 03796ac52be3d00fcb310d0d4269f757ff6b2d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Mon, 20 Apr 2026 14:30:32 -0300 Subject: [PATCH 092/101] Feature/pdf layout anonymization (#76) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat(extractors): use pymupdf layout for pdf text extraction * ✨ feat(normalization): enhance document normalization to preserve paragraph structure * 📝 docs: document default values for extractor and normalization helpers * 🩹 fix(extractors): use pymupdf4llm.to_text with page_chunks for pdf paragraphs * ♻️ Add DOCX and PDF anonymizer modules - Implemented DocxAnonymizer class to handle anonymization of DOCX documents by replacing sensitive data with label tokens. This includes functionality for unzipping documents, parsing XML, editing content, and adding watermarks. - Developed PdfAnonymizer class for anonymizing PDF documents, utilizing pymupdf for document manipulation. This includes layout parsing, font caching, redaction operations, and watermarking. * 🔧 Enhance PDF and DOCX handling in anonymization process * 📝 Update backend module references for document rendering in README * ✅ Update tests to use DOCX format for document anonymization and enhance mock behavior * ✨ Add end-to-end PDF anonymization notebook with PyMuPDF and AymurAI API * ♻️ Rework PDF anonymization for precise spans and widget handling * 🔧 Update model_dump calls to exclude None values for improved data handling * 📝 Add docstrings to label replacement functions * ♻️ Refactor watermark handling and optimize PDF token aliasing * ✅ Add integration tests for merging fragmented numeric labels and excluding null alt attributes in PDF anonymization * ➖ Remove opencv-python-headless dependency from project requirements * ♻️ Implement paragraph splitting function to enhance document text extraction * 🔧 Update dependency installation command to prevent Python downloads * 🔥 Remove redundant tests for merging fragmented numeric labels and PDF anonymization * ♻️ Refactor anonymizer tests to use DOCX format and enhance mock functionality * 🔧 Add xfail marker for PDF extraction test on Windows due to tensor type issue * ✨ Enhance PDF anonymization by adding cleanup rects, removing overlapping links, and scrubbing metadata * 🔧 Remove redundant return statement in _label_replacement_text function * ♻️ Refactor anonymization module: split pdf and docx internals by format * ✅ Add integration tests for PDF and DOCX anonymizers, including metadata scrubbing and link preservation * ✨ Add watermark layout adjustments to avoid footer content overlap in PDF anonymization * ✅ Add integration test to ensure watermark is positioned away from footer content in PDF anonymization * 🩹 Fix: read docx xml as utf-8 across platforms * ✅ Add Windows-specific xfail marker for PDF tests and implement UTF-8 XML reading test --- .github/workflows/pytest.yml | 2 +- .../routers/anonymizer/anonymizer.py | 102 ++- .../routers/misc/document_extract.py | 21 +- .../database/crud/anonymization/paragraph.py | 8 +- aymurai/settings.py | 4 + aymurai/text/anonymization/__init__.py | 18 +- aymurai/text/anonymization/alignment.py | 81 +- aymurai/text/anonymization/base.py | 79 ++ aymurai/text/anonymization/doc_anonymizer.py | 88 -- aymurai/text/anonymization/docx/__init__.py | 3 + aymurai/text/anonymization/docx/anonymizer.py | 122 +++ .../{watermarks.py => docx/watermark.py} | 0 .../{xml_docx.py => docx/xml.py} | 0 aymurai/text/anonymization/pdf/__init__.py | 3 + aymurai/text/anonymization/pdf/anonymizer.py | 100 +++ aymurai/text/anonymization/pdf/common.py | 620 +++++++++++++ aymurai/text/anonymization/pdf/layout.py | 510 +++++++++++ aymurai/text/anonymization/pdf/ops.py | 828 ++++++++++++++++++ aymurai/text/anonymization/pdf/sanitize.py | 294 +++++++ aymurai/text/anonymization/pdf/watermark.py | 522 +++++++++++ aymurai/text/anonymization/pdf/widgets.py | 323 +++++++ aymurai/text/extractors/pdf.py | 20 +- aymurai/text/extractors/utils.py | 146 ++- aymurai/text/normalize.py | 67 +- docs/es/pipelines/anonymizer/README.md | 2 +- docs/pipelines/anonymizer/README.md | 2 +- .../pdf-support/06-pymupdf-layout.ipynb | 253 ++++++ .../api/routers/anonymizer/test_anonymizer.py | 423 ++++++++- .../api/routers/misc/test_document_extract.py | 6 + tests/api/routers/test_pipeline_flows.py | 35 +- 30 files changed, 4392 insertions(+), 290 deletions(-) create mode 100644 aymurai/text/anonymization/base.py delete mode 100644 aymurai/text/anonymization/doc_anonymizer.py create mode 100644 aymurai/text/anonymization/docx/__init__.py create mode 100644 aymurai/text/anonymization/docx/anonymizer.py rename aymurai/text/anonymization/{watermarks.py => docx/watermark.py} (100%) rename aymurai/text/anonymization/{xml_docx.py => docx/xml.py} (100%) create mode 100644 aymurai/text/anonymization/pdf/__init__.py create mode 100644 aymurai/text/anonymization/pdf/anonymizer.py create mode 100644 aymurai/text/anonymization/pdf/common.py create mode 100644 aymurai/text/anonymization/pdf/layout.py create mode 100644 aymurai/text/anonymization/pdf/ops.py create mode 100644 aymurai/text/anonymization/pdf/sanitize.py create mode 100644 aymurai/text/anonymization/pdf/watermark.py create mode 100644 aymurai/text/anonymization/pdf/widgets.py create mode 100644 notebooks/experiments/pdf-support/06-pymupdf-layout.ipynb diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 4636bc5c..319cd7f0 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -64,7 +64,7 @@ jobs: - name: Install dependencies run: | - uv sync --frozen --python python --no-dev --no-managed-python --group tests + uv sync --frozen --python python --no-dev --no-python-downloads --group tests - name: Run api tests env: diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 65a36131..6a48c33b 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -5,7 +5,7 @@ from threading import Lock import torch -from fastapi import Body, Depends, Form, Query, UploadFile +from fastapi import Body, Depends, Form, HTTPException, Query, UploadFile from fastapi.responses import FileResponse from fastapi.routing import APIRouter from sqlmodel import Session @@ -31,7 +31,10 @@ TextRequest, ) from aymurai.settings import settings -from aymurai.text.anonymization import DocAnonymizer, replace_labels_in_text +from aymurai.text.anonymization import ( + InvalidDocumentAnonymizer, + get_anonymizer, +) from aymurai.text.extraction import MIMETYPE_EXTENSION_MAPPER from aymurai.utils.entity_disambiguation import ( build_canonical_entities, @@ -514,11 +517,21 @@ async def anonymizer_compile_document( """ logger.info(f"receiving => {file.filename}") extension = MIMETYPE_EXTENSION_MAPPER.get(file.content_type) - logger.info(f"detection extension: {extension} ({file.content_type})") + file_suffix = os.path.splitext(file.filename or "")[1].lower() + + if extension is None and file_suffix: + extension = file_suffix.lstrip(".") + + if extension not in {"docx", "pdf"}: + raise HTTPException( + status_code=400, + detail=f"Unsupported format for anonymization: {extension or 'unknown'}", + ) + + logger.info(f"detected extension: {extension} ({file.content_type})") # Create a temporary file - _, suffix = os.path.splitext(file.filename) - suffix = suffix if suffix == ".docx" else ".txt" + suffix = f".{extension}" tmp_dir = tempfile.gettempdir() # Use delete=False to avoid the file being deleted when the NamedTemporaryFile object is closed @@ -537,7 +550,7 @@ async def anonymizer_compile_document( annots_json = json.loads(annotations) annots = DocumentAnnotations.model_validate(annots_json) - logger.info(f"processing annotations => {annots}") + effective_label_policies = _merge_label_policies(annots.label_policies) effective_render_policy = _merge_render_policy(annots.render_policy) @@ -562,9 +575,6 @@ async def anonymizer_compile_document( override=False, ) - # Anonymize the document - doc_anonymizer = DocAnonymizer() - filtered_annotations = [] for paragraph in annots.data: filtered_labels = [ @@ -583,39 +593,36 @@ async def anonymizer_compile_document( filtered_annotations, effective_render_policy, effective_label_policies ) - if suffix == ".docx": - item = {"path": tmp_filename} - doc_anonymizer.render_context = render_context - doc_anonymizer( - item, - [ - document_information.model_dump() - for document_information in filtered_annotations - ], + preds = [ + document_information.model_dump(mode="json", exclude_none=True) + for document_information in filtered_annotations + ] + + try: + anonymizer = get_anonymizer(extension) + anonymized_path = anonymizer( + {"path": tmp_filename}, + preds, tmp_dir, + render_context=render_context, + ) + except (ValueError, InvalidDocumentAnonymizer) as exc: + if os.path.exists(tmp_filename): + os.remove(tmp_filename) + raise HTTPException(status_code=400, detail=str(exc)) from exc + + if extension == "pdf": + if os.path.exists(tmp_filename): + os.remove(tmp_filename) + + return FileResponse( + anonymized_path, + background=BackgroundTask(os.remove, anonymized_path), + media_type="application/pdf", + filename=f"{os.path.splitext(file.filename)[0]}.pdf", ) - logger.info(f"saved temp file on local storage => {tmp_filename}") - - else: - # Export as raw document - anonymized_doc = [ - replace_labels_in_text( - document_information.model_dump(), - render_context=render_context, - ) - .replace("<", "<") - .replace(">", ">") - for document_information in filtered_annotations - ] - with open(tmp_filename, "w") as f: - f.write("\n".join(anonymized_doc)) - - # Add watermark to the end of the document - f.write( - "\n\nDocumento anonimizado por AymurAI\n\nhttps://www.aymurai.info/" - ) - # Convert to ODT + # DOCX flow keeps ODT output cmd = [ settings.LIBREOFFICE_BIN, "--headless", @@ -623,9 +630,8 @@ async def anonymizer_compile_document( "odt", "--outdir", tmp_dir, - tmp_filename, + anonymized_path, ] - logger.info(f"Executing: {' '.join(cmd)}") try: @@ -633,20 +639,20 @@ async def anonymizer_compile_document( cmd, shell=False, encoding="utf-8", errors="ignore" ) logger.info(f"LibreOffice output: {output}") - except subprocess.CalledProcessError as e: + except subprocess.CalledProcessError as exc: raise RuntimeError( - f"LibreOffice conversion failed: {e.output.decode('utf-8', errors='ignore')}" - ) + f"LibreOffice conversion failed: {exc.output.decode('utf-8', errors='ignore')}" + ) from exc + finally: + if os.path.exists(tmp_filename): + os.remove(tmp_filename) - odt = tmp_filename.replace(suffix, ".odt") + odt = f"{os.path.splitext(anonymized_path)[0]}.odt" logger.info(f"Expected output file path: {odt}") if not os.path.exists(odt): raise RuntimeError(f"File at path {odt} does not exist.") - # Ensure the temporary file is deleted - os.remove(tmp_filename) - return FileResponse( odt, background=BackgroundTask(os.remove, odt), diff --git a/aymurai/api/endpoints/routers/misc/document_extract.py b/aymurai/api/endpoints/routers/misc/document_extract.py index 37b7d0a4..ba315b74 100644 --- a/aymurai/api/endpoints/routers/misc/document_extract.py +++ b/aymurai/api/endpoints/routers/misc/document_extract.py @@ -31,7 +31,7 @@ def extraction(path: str) -> str: str: Extracted text from the document. """ text = extract_document(path) - return document_normalize(text) if text else "" + return document_normalize(text, preserve_paragraphs=True) if text else "" def run_safe_text_extraction( @@ -63,6 +63,20 @@ def run_safe_text_extraction( raise +def _split_document_paragraphs(document: str) -> list[str]: + if re.search(r"\n\s*\n+", document): + raw_paragraphs = re.split(r"\n\s*\n+", document) + else: + raw_paragraphs = document.splitlines() + + paragraphs = [ + re.sub(r"[ \t]{2,}", " ", paragraph.strip()) + for paragraph in raw_paragraphs + if paragraph.strip() + ] + return list(unique_justseen(paragraphs)) + + @router.post("/document-extract", response_model=Document) def plain_text_extractor(file: UploadFile) -> Document: """ @@ -111,9 +125,6 @@ def plain_text_extractor(file: UploadFile) -> Document: logger.info(f"removed temp file from local storage => {tmp_filename}") document_id = data_to_uuid(data) - - paragraphs = [line.strip() for line in document.split("\n") if line.strip()] - paragraphs = [re.sub(r"\s{2,}", " ", line) for line in paragraphs] - paragraphs = list(unique_justseen(paragraphs)) + paragraphs = _split_document_paragraphs(document) return Document(document=paragraphs, document_id=document_id) diff --git a/aymurai/database/crud/anonymization/paragraph.py b/aymurai/database/crud/anonymization/paragraph.py index 1d169036..17f826b8 100644 --- a/aymurai/database/crud/anonymization/paragraph.py +++ b/aymurai/database/crud/anonymization/paragraph.py @@ -27,7 +27,7 @@ def _serialize_doclabels(value: list[DocLabel] | None): """ if value is None: return None - return _DOC_LABELS_ADAPTER.dump_python(value, mode="json") + return _DOC_LABELS_ADAPTER.dump_python(value, mode="json", exclude_none=True) def _normalize_paragraph_payload(payload: dict) -> dict: @@ -63,7 +63,7 @@ def anonymization_paragraph_create( Returns: AnonymizationParagraph: The persisted paragraph record. """ - payload = _normalize_paragraph_payload(paragraph_in.model_dump()) + payload = _normalize_paragraph_payload(paragraph_in.model_dump(exclude_none=True)) new_paragraph = AnonymizationParagraph(**payload) if override: @@ -171,14 +171,14 @@ def anonymization_paragraph_batch_create_update( paragraph = session.get(AnonymizationParagraph, paragraph_id) if paragraph: - payload = _normalize_paragraph_payload(p_in.model_dump()) + payload = _normalize_paragraph_payload(p_in.model_dump(exclude_none=True)) payload.pop("id", None) for field, value in payload.items(): if value is not None: setattr(paragraph, field, value) else: - payload = _normalize_paragraph_payload(p_in.model_dump()) + payload = _normalize_paragraph_payload(p_in.model_dump(exclude_none=True)) paragraph = AnonymizationParagraph(**payload) session.add(paragraph) diff --git a/aymurai/settings.py b/aymurai/settings.py index 3844d767..2079f561 100644 --- a/aymurai/settings.py +++ b/aymurai/settings.py @@ -65,6 +65,10 @@ def assemble_cors_origins(cls, v) -> list[str]: MEMORY_CACHE_TTL: int = 60 LIBREOFFICE_BIN: str = "libreoffice" + PDF_WATERMARK_FONT_REGULAR: str | None = None + PDF_WATERMARK_FONT_BOLD: str | None = None + ANONYMIZATION_METADATA_CREATOR: str = "AymurAI" + ANONYMIZATION_METADATA_PRODUCER: str = "AymurAI" # Disambiguation Config diff --git a/aymurai/text/anonymization/__init__.py b/aymurai/text/anonymization/__init__.py index 7f839a95..51f3a65b 100644 --- a/aymurai/text/anonymization/__init__.py +++ b/aymurai/text/anonymization/__init__.py @@ -1,7 +1,21 @@ from aymurai.text.anonymization.alignment import replace_labels_in_text -from aymurai.text.anonymization.doc_anonymizer import DocAnonymizer +from aymurai.text.anonymization.base import ( + BaseAnonymizer, + InvalidDocumentAnonymizer, + get_anonymizer, + register_anonymizer, + supported_extensions, +) +from aymurai.text.anonymization.docx import DocxAnonymizer +from aymurai.text.anonymization.pdf import PdfAnonymizer __all__ = [ - "DocAnonymizer", + "BaseAnonymizer", + "DocxAnonymizer", + "PdfAnonymizer", + "InvalidDocumentAnonymizer", + "get_anonymizer", + "register_anonymizer", + "supported_extensions", "replace_labels_in_text", ] diff --git a/aymurai/text/anonymization/alignment.py b/aymurai/text/anonymization/alignment.py index 3a6386b3..e4f2547e 100644 --- a/aymurai/text/anonymization/alignment.py +++ b/aymurai/text/anonymization/alignment.py @@ -9,9 +9,9 @@ from joblib import hash from more_itertools import flatten +from aymurai.meta.api_interfaces import LabelPolicy from aymurai.models.flair.utils import FlairTextNormalize from aymurai.utils.alignment.core import align_text, tokenize -from aymurai.meta.api_interfaces import LabelPolicy REGEX_PARAGRAPH = r"((?.*?)(\/w:p\b)" REGEX_FRAGMENT = r"(?(?P.*?)(<.*?\/w:t)" @@ -61,6 +61,71 @@ def resolve_render_token(label: dict, render_context: dict | None = None) -> str return f"{base}_{index}" +def _label_replacement_start(label: dict) -> int: + """ + Determines the start character index for a label, considering possible alternative attributes. + + Args: + label (dict): Label dictionary which may contain alternative start character attributes. + + Returns: + int: The start character index for the label. + """ + attrs = label.get("attrs") or {} + alt_start = attrs.get("aymurai_alt_start_char") + start_char = label.get("start_char") + return int(alt_start if alt_start is not None else (start_char or 0)) + + +def _label_replacement_end(label: dict) -> int: + """ + Determines the end character index for a label, considering possible alternative attributes. + + Args: + label (dict): Label dictionary which may contain alternative end character attributes. + + Returns: + int: The end character index for the label. + """ + attrs = label.get("attrs") or {} + alt_end = attrs.get("aymurai_alt_end_char") + end_char = label.get("end_char") + return int(alt_end if alt_end is not None else (end_char or 0)) + + +def _label_replacement_text(label: dict, document: str) -> str: + """ + Determines the replacement text for a label, considering possible alternative attributes. + + Args: + label (dict): Label dictionary which may contain alternative text attributes. + document (str): The document text from which to extract the label text. + + Returns: + str: The text for the label, considering possible alternative attributes. + """ + attrs = label.get("attrs") or {} + + alt_text = attrs.get("aymurai_alt_text") + if alt_text is not None: + return str(alt_text) if alt_text else "" + + alt_start = attrs.get("aymurai_alt_start_char") + alt_end = attrs.get("aymurai_alt_end_char") + if alt_start is not None and alt_end is not None: + start_char, end_char = int(alt_start), int(alt_end) + if 0 <= start_char < end_char <= len(document): + return document[start_char:end_char] + + start_char = int(label.get("start_char") or 0) + end_char = int(label.get("end_char") or 0) + if 0 <= start_char < end_char <= len(document): + return document[start_char:end_char] + + text = label.get("text") + return str(text) if text else "" + + def unify_consecutive_labels( sample: dict, text_key: str = "document", @@ -93,9 +158,11 @@ def unify_consecutive_labels( # Iterate over labels for label in labels: # Get attributes - text = label["attrs"]["aymurai_alt_text"] or label["text"] - start_char = label["attrs"]["aymurai_alt_start_char"] or label["start_char"] - end_char = label["attrs"]["aymurai_alt_end_char"] or label["end_char"] + text = _label_replacement_text(label, document) + start_char = _label_replacement_start(label) + end_char = _label_replacement_end(label) + if not text or end_char <= start_char: + continue aymurai_label = resolve_render_token(label, render_context) if current_group is None: @@ -115,7 +182,7 @@ def unify_consecutive_labels( else: # Finish the current group and start a new one current_group["text"] = document[ - current_group["start_char"] : current_group["end_char"] + 1 + current_group["start_char"] : current_group["end_char"] ] unified_labels.append(current_group) current_group = { @@ -128,7 +195,7 @@ def unify_consecutive_labels( # Finish the last group if current_group is not None: current_group["text"] = document[ - current_group["start_char"] : current_group["end_char"] + 1 + current_group["start_char"] : current_group["end_char"] ] unified_labels.append(current_group) @@ -271,7 +338,7 @@ def index_paragraphs(file: str) -> list[dict]: list[dict]: A list of dictionaries representing the indexed paragraphs. """ # Read the XML file - with open(file) as f: + with open(file, encoding="utf-8-sig") as f: xml = f.read() paragraphs = [] diff --git a/aymurai/text/anonymization/base.py b/aymurai/text/anonymization/base.py new file mode 100644 index 00000000..a1631159 --- /dev/null +++ b/aymurai/text/anonymization/base.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from pathlib import Path +from typing import Any + + +class InvalidDocumentAnonymizer(Exception): + """Raised when an anonymizer receives an invalid or unsupported document.""" + + +class BaseAnonymizer(ABC): + """Common interface shared by all document anonymizers.""" + + extension: str + + @property + def __name__(self) -> str: + return self.__class__.__name__ + + def ensure_file(self, path: Path) -> Path: + if not path.exists(): + raise InvalidDocumentAnonymizer(f"Invalid path: {path}") + return path + + def __call__( + self, + item: dict, + preds: list[dict], + output_dir: str = ".", + render_context: dict[str, Any] | None = None, + ) -> str: + return self.anonymize(item, preds, output_dir, render_context=render_context) + + @abstractmethod + def anonymize( + self, + item: dict, + preds: list[dict], + output_dir: str = ".", + render_context: dict[str, Any] | None = None, + ) -> str: + """Anonymize a document and return the output path.""" + + +_REGISTRY: dict[str, type[BaseAnonymizer]] = {} + + +def register_anonymizer(cls: type[BaseAnonymizer]) -> type[BaseAnonymizer]: + extension = getattr(cls, "extension", None) + if not extension: + raise ValueError( + f"Anonymizer {cls.__name__} must define an 'extension' attribute" + ) + + _REGISTRY[extension.lower()] = cls + return cls + + +def get_anonymizer(extension: str) -> BaseAnonymizer: + normalized = extension.lower() + try: + anonymizer_cls = _REGISTRY[normalized] + except KeyError as exc: + raise ValueError(f"Unsupported extension: {extension}") from exc + return anonymizer_cls() + + +def supported_extensions() -> set[str]: + return set(_REGISTRY.keys()) + + +__all__ = [ + "BaseAnonymizer", + "InvalidDocumentAnonymizer", + "get_anonymizer", + "register_anonymizer", + "supported_extensions", +] diff --git a/aymurai/text/anonymization/doc_anonymizer.py b/aymurai/text/anonymization/doc_anonymizer.py deleted file mode 100644 index 7feb6f3a..00000000 --- a/aymurai/text/anonymization/doc_anonymizer.py +++ /dev/null @@ -1,88 +0,0 @@ -import os -import tempfile -from glob import glob - -from more_itertools import flatten - -from aymurai.meta.pipeline_interfaces import Transform -from aymurai.text.anonymization.alignment import ( - index_paragraphs, - match_paragraphs_with_predictions, -) -from aymurai.text.anonymization.watermarks import add_footer_watermark -from aymurai.text.anonymization.xml_docx import ( - create_docx, - replace_text_in_xml, - unzip_document, -) -from aymurai.utils.cache import cache_load, cache_save, get_cache_key - - -class DocAnonymizer(Transform): - """ - Anonymize document by replacing sensitive data with label tokens - """ - - def __init__(self, use_cache: bool = False): - self.use_cache = use_cache - self.render_context = None - - def __call__(self, item: dict, preds: list[dict], output_dir: str = ".") -> None: - """ - Performs the anonymization process on a document. - - Args: - item (dict): The document item to be anonymized. - preds (list[dict]): The list of predictions for the document. - output_dir (str, optional): The directory to save the anonymized document. - Defaults to ".". - - Raises: - ValueError: If the document has an extension other than `.docx`. - """ - item_path = item["path"] - - if not os.path.splitext(item_path)[-1] == ".docx": - raise ValueError("Only `.docx` extension is allowed.") - - if not item.get("data"): - item["data"] = {} - - cache_key = get_cache_key(item_path, self.__name__) - if self.use_cache and (cache_data := cache_load(key=cache_key)): - paragraphs = cache_data - else: - # Unzip document into a temporary directory - with tempfile.TemporaryDirectory() as tempdir: - unzip_document(item_path, tempdir) - - # Parse XML files - xml_files = glob(f"{tempdir}/**/*.xml", recursive=True) - paragraphs = (index_paragraphs(file) for file in xml_files) - paragraphs = list(flatten(paragraphs)) - - # Filter out empty paragraphs - paragraphs = [ - paragraph - for paragraph in paragraphs - if paragraph["plain_text"].strip() - ] - - # Matching - paragraphs = match_paragraphs_with_predictions(paragraphs, preds) - - # Edit XML filess - replace_text_in_xml(paragraphs, tempdir, self.render_context) - - # Recreate anonymized document - os.makedirs(output_dir, exist_ok=True) - create_docx( - tempdir, - f"{output_dir}/{os.path.basename(item_path)}", - ) - - # Add watermark to the footer - add_footer_watermark(f"{output_dir}/{os.path.basename(item_path)}") - - if self.use_cache: - cache_save(paragraphs, key=cache_key) diff --git a/aymurai/text/anonymization/docx/__init__.py b/aymurai/text/anonymization/docx/__init__.py new file mode 100644 index 00000000..5d5d0aca --- /dev/null +++ b/aymurai/text/anonymization/docx/__init__.py @@ -0,0 +1,3 @@ +from aymurai.text.anonymization.docx.anonymizer import DocxAnonymizer + +__all__ = ["DocxAnonymizer"] diff --git a/aymurai/text/anonymization/docx/anonymizer.py b/aymurai/text/anonymization/docx/anonymizer.py new file mode 100644 index 00000000..73c43487 --- /dev/null +++ b/aymurai/text/anonymization/docx/anonymizer.py @@ -0,0 +1,122 @@ +import os +import tempfile +from datetime import datetime, timezone +from glob import glob +from pathlib import Path +from typing import Any + +from docx import Document +from more_itertools import flatten + +from aymurai.text.anonymization.alignment import ( + index_paragraphs, + match_paragraphs_with_predictions, +) +from aymurai.text.anonymization.base import ( + BaseAnonymizer, + InvalidDocumentAnonymizer, + register_anonymizer, +) +from aymurai.text.anonymization.docx.watermark import add_footer_watermark +from aymurai.settings import settings +from aymurai.text.anonymization.docx.xml import ( + create_docx, + replace_text_in_xml, + unzip_document, +) +from aymurai.utils.cache import cache_load, cache_save, get_cache_key + + +def _set_aymurai_core_properties(doc_path: str) -> None: + """ + Applies the configured AymurAI tooling metadata fields to the DOCX core properties. + + Args: + doc_path (str): The path to the DOCX document to update. + """ + document = Document(doc_path) + core_properties = document.core_properties + core_properties.author = "" + core_properties.last_modified_by = settings.ANONYMIZATION_METADATA_CREATOR + core_properties.modified = datetime.now(timezone.utc) + document.save(doc_path) + + +@register_anonymizer +class DocxAnonymizer(BaseAnonymizer): + """ + Anonymize DOCX documents by replacing sensitive data with label tokens. + """ + + extension = "docx" + + def __init__(self, use_cache: bool = False): + self.use_cache = use_cache + + def anonymize( + self, + item: dict, + preds: list[dict], + output_dir: str = ".", + render_context: dict[str, Any] | None = None, + ) -> str: + """ + Anonymizes a DOCX document using the matched paragraph predictions. + + Args: + item (dict): The item dictionary containing the input DOCX path. + preds (list[dict]): The predictions to apply to the document. + output_dir (str, optional): The directory where the anonymized document should be written. Defaults to '.'. + render_context (dict[str, Any] | None, optional): The rendering context used to resolve replacement tokens. + Defaults to None. + + Returns: + str: The path to the anonymized DOCX output file. + """ + item_path = Path(item["path"]) + file_path = self.ensure_file(item_path) + + if file_path.suffix.lower() != ".docx": + raise InvalidDocumentAnonymizer("Only `.docx` extension is allowed.") + + if not item.get("data"): + item["data"] = {} + + cache_key = get_cache_key(str(file_path), self.__name__) + if self.use_cache and (cache_data := cache_load(key=cache_key)): + paragraphs = cache_data + else: + # Unzip document into a temporary directory + with tempfile.TemporaryDirectory() as tempdir: + unzip_document(str(file_path), tempdir) + + # Parse XML files + xml_files = glob(f"{tempdir}/**/*.xml", recursive=True) + paragraphs = (index_paragraphs(file) for file in xml_files) + paragraphs = list(flatten(paragraphs)) + + # Filter out empty paragraphs + paragraphs = [ + paragraph + for paragraph in paragraphs + if paragraph["plain_text"].strip() + ] + # Matching + paragraphs = match_paragraphs_with_predictions(paragraphs, preds) + + # Edit XML files + replace_text_in_xml(paragraphs, tempdir, render_context) + + # Recreate anonymized document + os.makedirs(output_dir, exist_ok=True) + output_path = f"{output_dir}/{os.path.basename(str(file_path))}" + create_docx(tempdir, output_path) + + # Add metadata branding and the footer watermark + _set_aymurai_core_properties(output_path) + add_footer_watermark(output_path) + + if self.use_cache: + cache_save(paragraphs, key=cache_key) + + return f"{output_dir}/{os.path.basename(str(file_path))}" diff --git a/aymurai/text/anonymization/watermarks.py b/aymurai/text/anonymization/docx/watermark.py similarity index 100% rename from aymurai/text/anonymization/watermarks.py rename to aymurai/text/anonymization/docx/watermark.py diff --git a/aymurai/text/anonymization/xml_docx.py b/aymurai/text/anonymization/docx/xml.py similarity index 100% rename from aymurai/text/anonymization/xml_docx.py rename to aymurai/text/anonymization/docx/xml.py diff --git a/aymurai/text/anonymization/pdf/__init__.py b/aymurai/text/anonymization/pdf/__init__.py new file mode 100644 index 00000000..21271aae --- /dev/null +++ b/aymurai/text/anonymization/pdf/__init__.py @@ -0,0 +1,3 @@ +from aymurai.text.anonymization.pdf.anonymizer import PdfAnonymizer + +__all__ = ["PdfAnonymizer"] diff --git a/aymurai/text/anonymization/pdf/anonymizer.py b/aymurai/text/anonymization/pdf/anonymizer.py new file mode 100644 index 00000000..0030c24b --- /dev/null +++ b/aymurai/text/anonymization/pdf/anonymizer.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +import os +from pathlib import Path +from typing import Any + +import pymupdf +import pymupdf.layout # noqa: F401 # activates layout support +from pymupdf4llm.helpers import document_layout as pymupdf4llm_document_layout + +from aymurai.text.anonymization.base import ( + BaseAnonymizer, + InvalidDocumentAnonymizer, + register_anonymizer, +) +from aymurai.text.anonymization.pdf.layout import ( + _apply_minimal_boundary_merge, + _build_layout_paragraphs, + _match_predictions_to_layout, +) +from aymurai.text.anonymization.pdf.ops import ( + _apply_redactions, + _collect_page_redactions, +) +from aymurai.text.anonymization.pdf.sanitize import ( + _collect_link_cleanup_rects, + _sanitize_document, +) +from aymurai.text.anonymization.pdf.watermark import add_pdf_footer_watermark + + +@register_anonymizer +class PdfAnonymizer(BaseAnonymizer): + """ + Anonymize PDF documents by replacing sensitive data with label tokens. + """ + + extension = "pdf" + + def anonymize( + self, + item: dict, + preds: list[dict], + output_dir: str = ".", + render_context: dict[str, Any] | None = None, + ) -> str: + """ + Anonymizes a PDF document using the matched paragraph predictions. + + Args: + item (dict): The item dictionary containing the input PDF path. + preds (list[dict]): The predictions to apply to the document. + output_dir (str, optional): The directory where the anonymized document should be written. Defaults to '.'. + render_context (dict[str, Any] | None, optional): The rendering context used to resolve replacement tokens. Defaults to None. + + Returns: + str: The path to the anonymized PDF output file. + """ + item_path = Path(item["path"]) + file_path = self.ensure_file(item_path) + + if file_path.suffix.lower() != ".pdf": + raise InvalidDocumentAnonymizer("Only `.pdf` extension is allowed.") + + with pymupdf.open(str(file_path)) as doc: + parsed_doc = pymupdf4llm_document_layout.parse_document( + doc, + filename=str(file_path), + show_progress=False, + force_text=True, + use_ocr=False, + force_ocr=False, + ) + + layout_paragraphs = _build_layout_paragraphs(parsed_doc) + matched_paragraphs = _match_predictions_to_layout( + layout_paragraphs, + preds, + ) + + _apply_minimal_boundary_merge(matched_paragraphs, render_context) + page_ops, widget_ops, signature_widget_ops = _collect_page_redactions( + doc, + matched_paragraphs, + render_context, + ) + _apply_redactions(doc, page_ops, widget_ops, signature_widget_ops) + cleanup_rects = _collect_link_cleanup_rects( + page_ops, + widget_ops, + signature_widget_ops, + ) + _sanitize_document(doc, cleanup_rects) + add_pdf_footer_watermark(doc) + + os.makedirs(output_dir, exist_ok=True) + output_path = Path(output_dir) / f"{file_path.stem}.anonymized.pdf" + doc.save(str(output_path), garbage=4, clean=1, deflate=1) + + return str(output_path) diff --git a/aymurai/text/anonymization/pdf/common.py b/aymurai/text/anonymization/pdf/common.py new file mode 100644 index 00000000..91f42927 --- /dev/null +++ b/aymurai/text/anonymization/pdf/common.py @@ -0,0 +1,620 @@ +from __future__ import annotations + +import re +from functools import lru_cache +from typing import Any +from unicodedata import normalize + +import pymupdf + +TEXT_FLAG_ITALIC = 2 +TEXT_FLAG_SERIF = 4 +TEXT_FLAG_MONOSPACED = 8 +TEXT_FLAG_BOLD = 16 +PDF_TAG_MIN_FONT_SIZE = 7.0 +PDF_TAG_FONT_STEP = 0.5 +PDF_TAG_MAX_ABBREVIATION = 3 +PDF_TOKEN_ALIAS_MAP: dict[str, tuple[str, str]] = { + "CORREO_ELECTRONICO": ("CORREO", "MAIL"), + "CUIT_CUIL": ("CUIT", "CUIL"), + "DIRECCION": ("DIREC", "DIR"), + "ESTUDIOS": ("ESTUD", "EDU"), + "MARCA_AUTOMOVIL": ("MARCA_AUTO", "AUTO"), + "NACIONALIDAD": ("NACIONAL", "NAC"), + "NOMBRE_ARCHIVO": ("NOM_ARCH", "ARCH"), + "NUM_ACTUACION": ("NUM_ACT", "ACT"), + "NUM_CAJA_AHORRO": ("NUM_CAJA", "CAJA"), + "NUM_EXPEDIENTE": ("NUM_EXP", "EXPTE"), + "NUM_MATRICULA": ("NUM_MAT", "MAT"), + "PATENTE_DOMINIO": ("PAT_DOM", "PAT"), + "TELEFONO": ("TELEF", "TEL"), + "TEXTO_ANONIMIZAR": ("TEXTO_ANON", "ANON"), + "USUARIX": ("USUAR", "USR"), +} +PDF_TAG_RECT_X_PADDING = 0.5 +PDF_TAG_RECT_Y_PADDING = 0.0 +PDF_TAG_RECT_INSET = 0.5 +PDF_TAG_RECT_GAP_FACTOR = 0.5 +PDF_TAG_RECT_GAP_MIN = 3.0 +PDF_TAG_RECT_GAP_MAX = 8.0 + + +def _line_text(line: dict) -> str: + """ + Builds the plain text content for a parsed PDF line. + + Args: + line (dict): The parsed line metadata being processed. + + Returns: + str: The concatenated text content for the line. + """ + return "".join(span.get("text", "") for span in line.get("spans", [])) + + +def _rect_tuple(value: Any) -> tuple[float, float, float, float]: + """ + Normalizes a rectangle-like value into a coordinate tuple. + + Args: + value (Any): The rectangle-like value to normalize. + + Returns: + tuple[float, float, float, float]: The normalized rectangle coordinates. + """ + if isinstance(value, pymupdf.Rect): + return (float(value.x0), float(value.y0), float(value.x1), float(value.y1)) + if isinstance(value, (list, tuple)) and len(value) == 4: + return (float(value[0]), float(value[1]), float(value[2]), float(value[3])) + raise ValueError(f"Invalid rectangle value: {value}") + + +def _default_style(fallback_size: float = 10.0) -> dict[str, Any]: + """ + Builds a default text style dictionary for PDF rendering helpers. + + Args: + fallback_size (float, optional): The fallback font size used when no style data is available. Defaults to 10.0. + + Returns: + dict[str, Any]: The default style dictionary. + """ + return { + "font": "", + "flags": 0, + "color": (0.0, 0.0, 0.0), + "size": fallback_size, + "ascender": 0.8, + "descender": -0.2, + } + + +def _span_text_weight(span: dict) -> tuple[int, float]: + """ + Computes a sorting weight for a span based on text length and size. + + Args: + span (dict): The span metadata being evaluated. + + Returns: + tuple[int, float]: The text-length and size weight for the span. + """ + text = str(span.get("text") or "").strip() + return (len(text), float(span.get("size") or 0.0)) + + +def _pdf_color_from_span(span: dict) -> tuple[float, float, float]: + """ + Converts a span color value into PDF RGB components. + + Args: + span (dict): The span metadata being evaluated. + + Returns: + tuple[float, float, float]: The PDF RGB color components for the span. + """ + try: + return tuple( + float(value) for value in pymupdf.sRGB_to_pdf(int(span.get("color") or 0)) + ) + except Exception: + return (0.0, 0.0, 0.0) + + +def _line_style(line: dict, fallback_size: float = 10.0) -> dict[str, Any]: + """ + Determines the dominant text style for a parsed PDF line. + + Args: + line (dict): The parsed line metadata being processed. + fallback_size (float, optional): The fallback font size used when no style data is available. Defaults to 10.0. + + Returns: + dict[str, Any]: The dominant style dictionary for the line. + """ + spans = [ + span for span in line.get("spans") or [] if str(span.get("text") or "").strip() + ] + if not spans: + return _default_style(fallback_size) + + dominant = max(spans, key=_span_text_weight) + return { + "font": str(dominant.get("font") or ""), + "flags": int(dominant.get("flags") or 0), + "color": _pdf_color_from_span(dominant), + "size": float(dominant.get("size") or fallback_size), + "ascender": float(dominant.get("ascender") or 0.8), + "descender": float(dominant.get("descender") or -0.2), + } + + +def _build_spans_detail(line: dict) -> tuple[list[dict], int]: + """ + Builds per-span style metadata and character offsets for a line. + + Args: + line (dict): The parsed line metadata being processed. + + Returns: + tuple[list[dict], int]: The span detail list and left-strip offset. + """ + raw_text = normalize("NFKC", _line_text(line)) + strip_offset = len(raw_text) - len(raw_text.lstrip()) + + spans_detail: list[dict] = [] + cursor = 0 + for span in line.get("spans", []): + span_text = normalize("NFKC", span.get("text", "")) + span_start = cursor + cursor += len(span_text) + spans_detail.append( + { + "start": span_start, + "end": cursor, + "style": { + "font": str(span.get("font") or ""), + "flags": int(span.get("flags") or 0), + "color": _pdf_color_from_span(span), + "size": float(span.get("size") or 10.0), + "ascender": float(span.get("ascender") or 0.8), + "descender": float(span.get("descender") or -0.2), + }, + } + ) + return spans_detail, strip_offset + + +def _entity_style_from_spans( + line_entry: dict, + offset_in_stripped_text: int, +) -> dict[str, Any]: + """ + Resolves the style for the entity offset inside a line entry. + + Args: + line_entry (dict): The `line_entry` value used by this helper. + offset_in_stripped_text (int): The entity offset inside the stripped line text. + + Returns: + dict[str, Any]: The resolved style dictionary for the entity offset. + """ + spans_detail = line_entry.get("spans_detail") + if not spans_detail: + return line_entry.get("style") or _default_style() + + strip_offset = line_entry.get("strip_offset", 0) + raw_offset = offset_in_stripped_text + strip_offset + + for span_info in spans_detail: + if span_info["start"] <= raw_offset < span_info["end"]: + return span_info["style"] + + return line_entry.get("style") or _default_style() + + +def _font_size(line: dict, fallback: float = 10.0) -> float: + """ + Calculates a representative font size for a parsed line. + + Args: + line (dict): The parsed line metadata being processed. + fallback (float, optional): The fallback font size to use when the line has no span sizes. Defaults to 10.0. + + Returns: + float: The representative font size for the line. + """ + spans = line.get("spans") or [] + sizes = [float(span.get("size")) for span in spans if span.get("size")] + if not sizes: + return fallback + size = sum(sizes) / len(sizes) + return max(size * 0.9, PDF_TAG_MIN_FONT_SIZE) + + +def _style_flags(style: dict[str, Any]) -> tuple[bool, bool, bool, bool]: + """ + Extracts boolean style flags from a style dictionary. + + Args: + style (dict[str, Any]): The style dictionary being analyzed. + + Returns: + tuple[bool, bool, bool, bool]: The bold, italic, monospace, and serif flags. + """ + flags = int(style.get("flags") or 0) + font_label = str(style.get("font") or "").lower() + + is_bold = bool(flags & TEXT_FLAG_BOLD) or "bold" in font_label + is_italic = bool(flags & TEXT_FLAG_ITALIC) or any( + token in font_label for token in ("italic", "oblique") + ) + is_mono = bool(flags & TEXT_FLAG_MONOSPACED) or any( + token in font_label for token in ("courier", "mono", "console") + ) + is_serif = bool(flags & TEXT_FLAG_SERIF) or any( + token in font_label + for token in ("times", "serif", "georgia", "garamond", "mistral") + ) + return is_bold, is_italic, is_mono, is_serif + + +def _base14_fontname_for_style(style: dict[str, Any]) -> str: + """ + Maps a style dictionary to the closest Base-14 font name. + + Args: + style (dict[str, Any]): The style dictionary being analyzed. + + Returns: + str: The Base-14 font name that best matches the style. + """ + is_bold, is_italic, is_mono, is_serif = _style_flags(style) + + if is_mono: + family = "Courier" + elif is_serif: + family = "Times" + else: + family = "Helvetica" + + variants = { + ("Helvetica", False, False): "Helvetica", + ("Helvetica", True, False): "Helvetica-Bold", + ("Helvetica", False, True): "Helvetica-Oblique", + ("Helvetica", True, True): "Helvetica-BoldOblique", + ("Times", False, False): "Times-Roman", + ("Times", True, False): "Times-Bold", + ("Times", False, True): "Times-Italic", + ("Times", True, True): "Times-BoldItalic", + ("Courier", False, False): "Courier", + ("Courier", True, False): "Courier-Bold", + ("Courier", False, True): "Courier-Oblique", + ("Courier", True, True): "Courier-BoldOblique", + } + return variants[(family, is_bold, is_italic)] + + +def _build_flexible_pattern(text: str) -> str: + """ + Builds a whitespace-tolerant regex pattern for the given text. + + Args: + text (str): The text value being normalized or searched. + + Returns: + str: The whitespace-tolerant regex pattern. + """ + tokens = [re.escape(tok) for tok in re.split(r"\s+", text.strip()) if tok] + return r"\s+".join(tokens) + + +def _find_flexible( + haystack: str, + needle: str, + start: int = 0, +) -> tuple[int, int] | None: + """ + Finds a text span using exact and whitespace-tolerant matching. + + Args: + haystack (str): The source text to search within. + needle (str): The target text to search for. + start (int, optional): The preferred start offset for the search. Defaults to 0. + + Returns: + tuple[int, int] | None: The start and end offsets of the match, if found. + """ + if not needle: + return None + + idx = haystack.find(needle, start) + if idx >= 0: + return idx, idx + len(needle) + + pattern = _build_flexible_pattern(needle) + if not pattern: + return None + + match = re.search(pattern, haystack[start:]) + if match: + return start + match.start(), start + match.end() + + if start > 0: + match = re.search(pattern, haystack) + if match: + return match.start(), match.end() + + return None + + +def _token_parts(token: str) -> tuple[str, str | None]: + """ + Splits a logical token into its base label and numeric suffix. + + Args: + token (str): The logical replacement token being processed. + + Returns: + tuple[str, str | None]: The token base and optional numeric suffix. + """ + match = re.match(r"^(.*?)(?:_(\d+))?$", token) + if not match: + normalized = token.strip() or "ENT" + return normalized, None + + base = match.group(1).strip() or "ENT" + suffix = match.group(2) + return base, suffix + + +def _abbreviate_token(base: str, length: int) -> str: + """ + Builds an abbreviated token label with the requested length. + + Args: + base (str): The token base label to abbreviate or alias. + length (int): The target abbreviation length. + + Returns: + str: The abbreviated token label. + """ + normalized = "".join(char for char in base.upper() if char.isalnum()) + if not normalized: + normalized = "ENT" + return normalized[:length] or normalized[:1] or "E" + + +def _token_aliases(base: str) -> tuple[str, ...]: + """ + Returns configured alias labels for a token base. + + Args: + base (str): The token base label to abbreviate or alias. + + Returns: + tuple[str, ...]: The configured aliases for the token base. + """ + aliases = PDF_TOKEN_ALIAS_MAP.get(base.upper(), ()) + normalized_aliases: list[str] = [] + + for alias in aliases: + normalized = re.sub(r"[^A-Z0-9_]", "", str(alias).upper()) + if ( + normalized + and normalized != base.upper() + and normalized not in normalized_aliases + ): + normalized_aliases.append(normalized) + + return tuple(normalized_aliases) + + +def _build_display_token_candidates(token: str) -> list[str]: + """ + Builds the list of token display candidates to try when rendering. + + Args: + token (str): The logical replacement token being processed. + + Returns: + list[str]: The candidate display tokens to try when rendering. + """ + base, suffix = _token_parts(token.upper()) + candidates: list[str] = [] + + def add(value: str) -> None: + """ + Appends a token display candidate when it has not been added yet. + + Args: + value (str): The rectangle-like value to normalize. + """ + if value and value not in candidates: + candidates.append(value) + + def add_base_variants(label: str) -> None: + """ + Appends the base token variants for the current label candidate. + + Args: + label (str): The label metadata being processed. + """ + if suffix: + add(f"<{label}_{suffix}>") + add(f"<{label}>") + + add_base_variants(base) + + for alias in _token_aliases(base): + add_base_variants(alias) + + abbreviated = _abbreviate_token(base, PDF_TAG_MAX_ABBREVIATION) + add_base_variants(abbreviated) + + return candidates + + +def _iter_font_sizes(start_size: float) -> list[float]: + """ + Builds the descending font sizes to try when fitting a token. + + Args: + start_size (float): The `start_size` value used by this helper. + + Returns: + list[float]: The font sizes to try in descending order. + """ + if start_size <= 0: + return [] + + sizes: list[float] = [start_size] + current = start_size + while current - PDF_TAG_FONT_STEP >= PDF_TAG_MIN_FONT_SIZE - 1e-6: + current = round(current - PDF_TAG_FONT_STEP, 2) + if current not in sizes: + sizes.append(current) + + return sizes + + +def _fit_display_token( + token: str, + rect: pymupdf.Rect, + fontname: str, + base_font_size: float, + font_obj: pymupdf.Font | None = None, +) -> tuple[str | None, float | None]: + """ + Finds a token rendering variant and font size that fit inside a rectangle. + + Args: + token (str): The logical replacement token being processed. + rect (pymupdf.Rect): The rectangle used by the helper. + fontname (str): The font name to use for measurement or rendering. + base_font_size (float): The initial font size to try when fitting text. + font_obj (pymupdf.Font | None, optional): The font object used for measurement. Defaults to None. + + Returns: + tuple[str | None, float | None]: The fitted token text and font size. + """ + if rect.width <= 0 or rect.height <= 0: + return None, None + + available_width = max(rect.width - (2 * PDF_TAG_RECT_INSET), 1.0) + start_size = min(base_font_size, max(rect.height - 1.0, 1.0)) + if start_size < 1.0: + return None, None + + def _measure(text: str, size: float) -> float: + """ + Measures the width of a candidate token at the given font size. + + Args: + text (str): The text value being normalized or searched. + size (float): The font size used for the current measurement. + + Returns: + float: The measured width of the candidate text. + """ + if font_obj is not None: + try: + return font_obj.text_length(text, fontsize=size) + except Exception: + pass + return pymupdf.get_text_length(text, fontname=fontname, fontsize=size) + + for size in _iter_font_sizes(start_size): + for candidate in _build_display_token_candidates(token): + if _measure(candidate, size) <= available_width + 0.1: + return candidate, size + + return None, None + + +_BASE14_FONT_CACHE: dict[str, pymupdf.Font] = {} + + +@lru_cache(maxsize=None) +def _cached_base14_font(name: str) -> pymupdf.Font: + """ + Loads and caches a Base-14 font by name. + + Args: + name (str): The Base-14 font name to load. + + Returns: + pymupdf.Font: The cached Base-14 font object. + """ + return pymupdf.Font(name) + + +def _get_base14_font(style: dict[str, Any]) -> pymupdf.Font: + """ + Returns the cached Base-14 font object for a style dictionary. + + Args: + style (dict[str, Any]): The style dictionary being analyzed. + + Returns: + pymupdf.Font: The cached Base-14 font for the style. + """ + name = _base14_fontname_for_style(style) + font = _BASE14_FONT_CACHE.get(name) + if font is None: + font = _cached_base14_font(name) + _BASE14_FONT_CACHE[name] = font + return font + + +def _rect_vertical_overlap(left: pymupdf.Rect, right: pymupdf.Rect) -> float: + """ + Calculates the vertical overlap ratio between two rectangles. + + Args: + left (pymupdf.Rect): The left rectangle or label to compare. + right (pymupdf.Rect): The right rectangle or label to compare. + + Returns: + float: The vertical overlap ratio between the rectangles. + """ + overlap = max(0.0, min(left.y1, right.y1) - max(left.y0, right.y0)) + min_height = max(min(left.height, right.height), 1e-6) + return overlap / min_height + + +def _group_adjacent_rects( + rects: list[pymupdf.Rect], max_gap: float +) -> list[pymupdf.Rect]: + """ + Merges horizontally adjacent rectangles that belong to the same segment. + + Args: + rects (list[pymupdf.Rect]): The `rects` value used by this helper. + max_gap (float): The `max_gap` value used by this helper. + + Returns: + list[pymupdf.Rect]: The merged rectangle groups. + """ + if not rects: + return [] + + ordered = sorted(rects, key=lambda rect: (rect.y0, rect.x0, rect.x1)) + groups: list[list[pymupdf.Rect]] = [[ordered[0]]] + + for rect in ordered[1:]: + previous = groups[-1][-1] + gap = rect.x0 - previous.x1 + if _rect_vertical_overlap(previous, rect) >= 0.5 and gap <= max_gap: + groups[-1].append(rect) + else: + groups.append([rect]) + + merged_rects: list[pymupdf.Rect] = [] + for group in groups: + merged = pymupdf.Rect(group[0]) + for rect in group[1:]: + merged.include_rect(rect) + merged_rects.append(merged) + + return merged_rects diff --git a/aymurai/text/anonymization/pdf/layout.py b/aymurai/text/anonymization/pdf/layout.py new file mode 100644 index 00000000..50ce529a --- /dev/null +++ b/aymurai/text/anonymization/pdf/layout.py @@ -0,0 +1,510 @@ +from __future__ import annotations + +import re +from copy import deepcopy +from typing import Any +from unicodedata import normalize + +import pymupdf +from jiwer import cer + +from aymurai.logger import get_logger +from aymurai.text.anonymization.alignment import ( + _label_replacement_end as _label_end, +) +from aymurai.text.anonymization.alignment import ( + _label_replacement_start as _label_start, +) +from aymurai.text.anonymization.alignment import ( + resolve_render_token, +) +from aymurai.text.anonymization.pdf.common import ( + PDF_TAG_RECT_GAP_FACTOR, + PDF_TAG_RECT_GAP_MAX, + PDF_TAG_RECT_GAP_MIN, + _build_flexible_pattern, + _build_spans_detail, + _font_size, + _group_adjacent_rects, + _line_style, + _line_text, + _rect_tuple, + _rect_vertical_overlap, +) + +logger = get_logger(__name__) + + +def _same_boundary_candidate(left: dict, right: dict) -> bool: + """ + Checks whether two labels can share a merged boundary token. + + Args: + left (dict): The left rectangle or label to compare. + right (dict): The right rectangle or label to compare. + + Returns: + bool: Whether the labels can share a boundary token. + """ + left_attrs = left.get("attrs") or {} + right_attrs = right.get("attrs") or {} + + if left_attrs.get("aymurai_label") != right_attrs.get("aymurai_label"): + return False + + left_cid = left_attrs.get("canonical_entity_id") + right_cid = right_attrs.get("canonical_entity_id") + if left_cid and right_cid and str(left_cid) != str(right_cid): + return False + + left_text = str(left.get("text") or "").strip() + right_text = str(right.get("text") or "").strip() + return bool(left_text and right_text) + + +def _resolve_token(label: dict, render_context: dict[str, Any] | None) -> str: + """ + Resolves the logical replacement token for a label. + + Args: + label (dict): The label metadata being processed. + render_context (dict[str, Any] | None): The rendering context used to resolve replacement tokens. + + Returns: + str: The logical token that should replace the label. + """ + boundary_token = label.get("_boundary_token") + if boundary_token: + return boundary_token + + token = resolve_render_token(label, render_context) + return token or "ENT" + + +def _apply_minimal_boundary_merge( + paragraphs: list[dict], + render_context: dict[str, Any] | None, +) -> None: + """ + Propagates a shared token across paragraph-boundary label pairs. + + Args: + paragraphs (list[dict]): The paragraph collection being processed. + render_context (dict[str, Any] | None): The rendering context used to resolve replacement tokens. + """ + for left_par, right_par in zip(paragraphs, paragraphs[1:]): + left_doc = left_par.get("document") or "" + right_doc = right_par.get("document") or "" + left_labels = left_par.get("labels") or [] + right_labels = right_par.get("labels") or [] + + if not left_doc or not right_doc or not left_labels or not right_labels: + continue + + left_candidates = [ + label + for label in left_labels + if _label_end(label) >= max(0, len(left_doc) - 2) + ] + right_candidates = [label for label in right_labels if _label_start(label) <= 2] + + if not left_candidates or not right_candidates: + continue + + for left_label in left_candidates: + for right_label in right_candidates: + if not _same_boundary_candidate(left_label, right_label): + continue + + shared_token = _resolve_token(left_label, render_context) + if not shared_token: + shared_token = _resolve_token(right_label, render_context) + if shared_token: + left_label["_boundary_token"] = shared_token + right_label["_boundary_token"] = shared_token + break + + +def _build_layout_paragraphs(parsed_doc: Any) -> list[dict]: + """ + Builds normalized paragraph metadata from the parsed PDF layout. + + Args: + parsed_doc (Any): The parsed PDF layout document. + + Returns: + list[dict]: The normalized layout paragraphs extracted from the parsed document. + """ + chunks = parsed_doc.to_text( + page_chunks=True, + header=True, + footer=True, + show_progress=False, + ) + + paragraphs: list[dict] = [] + layout_index = 0 + for page_idx, (page, chunk) in enumerate(zip(parsed_doc.pages, chunks)): + page_text = chunk.get("text") or "" + page_boxes = chunk.get("page_boxes") or [] + + for box_meta in page_boxes: + box_idx = int(box_meta["index"]) + if box_idx >= len(page.boxes): + continue + + start, stop = box_meta.get("pos", (0, 0)) + box_text = normalize("NFKC", page_text[start:stop]).strip() + if not box_text: + continue + + box = page.boxes[box_idx] + line_entries: list[dict] = [] + line_text_chunks: list[str] = [] + line_cursor = 0 + + for line_idx, line in enumerate(box.textlines or []): + text = normalize("NFKC", _line_text(line)).strip() + if not text: + continue + + if line_text_chunks: + line_text_chunks.append("\n") + line_cursor += 1 + + line_start = line_cursor + line_text_chunks.append(text) + line_cursor += len(text) + line_end = line_cursor + style = _line_style(line) + spans_detail, strip_offset = _build_spans_detail(line) + + line_entries.append( + { + "page_index": page_idx, + "box_index": box_idx, + "line_index": line_idx, + "bbox": _rect_tuple(line["bbox"]), + "font_size": _font_size(line, float(style.get("size") or 10.0)), + "start": line_start, + "end": line_end, + "text": text, + "style": style, + "spans_detail": spans_detail, + "strip_offset": strip_offset, + } + ) + + line_text = "".join(line_text_chunks) + if not line_text: + continue + + paragraphs.append( + { + "plain_text": box_text, + "metadata": { + "layout_index": layout_index, + "page_index": page_idx, + "page_number": page.page_number, + "box_index": box_idx, + "boxclass": box.boxclass, + "box_bbox": ( + float(box.x0), + float(box.y0), + float(box.x1), + float(box.y1), + ), + "line_text": line_text, + "lines": line_entries, + }, + } + ) + layout_index += 1 + + return paragraphs + + +def _match_predictions_to_layout( + layout_paragraphs: list[dict], + preds: list[dict], +) -> list[dict]: + """ + Matches model predictions to the closest layout paragraphs. + + Args: + layout_paragraphs (list[dict]): The `layout_paragraphs` value used by this helper. + preds (list[dict]): The predictions to apply to the document. + + Returns: + list[dict]: The predictions annotated with their matched layout metadata. + """ + if not layout_paragraphs or not preds: + return [] + + available_indices = list(range(len(layout_paragraphs))) + all_indices = list(range(len(layout_paragraphs))) + matched: list[dict] = [] + + normalized_layout_texts = [ + normalize("NFKC", paragraph["plain_text"]).strip() + for paragraph in layout_paragraphs + ] + + for pred_idx, pred in enumerate(preds): + pred_text = normalize("NFKC", str(pred.get("document") or "")).strip() + if not pred_text: + continue + + candidate_pool = available_indices if available_indices else all_indices + exact_idx = next( + ( + idx + for idx in candidate_pool + if normalized_layout_texts[idx] == pred_text + ), + None, + ) + + if exact_idx is None: + exact_idx = min( + candidate_pool, + key=lambda idx: cer(pred_text, normalized_layout_texts[idx]), + ) + + paragraph = deepcopy(layout_paragraphs[exact_idx]) + paragraph["document"] = pred.get("document") or "" + paragraph["labels"] = pred.get("labels") or [] + paragraph["pred_index"] = pred_idx + matched.append(paragraph) + + if exact_idx in available_indices: + available_indices.remove(exact_idx) + + matched.sort(key=lambda paragraph: paragraph["metadata"]["layout_index"]) + return matched + + +def _pick_rect_group_for_segment( + page: pymupdf.Page, + line: dict, + text: str, + line_x_cursor: dict[tuple[int, int, int], float], +) -> pymupdf.Rect: + """ + Chooses the best rectangle group for a text segment on the page. + + Args: + page (pymupdf.Page): The PDF page being processed. + line (dict): The parsed line metadata being processed. + text (str): The text value being normalized or searched. + line_x_cursor (dict[tuple[int, int, int], float]): The per-line cursor used to keep page searches stable. + + Returns: + pymupdf.Rect | None: The chosen rectangle group for the segment, if found. + """ + clip = pymupdf.Rect(line["bbox"]) + rects = [rect for rect in page.search_for(text, clip=clip) if rect.intersects(clip)] + if not rects: + return clip + + max_gap = min( + max(clip.height * PDF_TAG_RECT_GAP_FACTOR, PDF_TAG_RECT_GAP_MIN), + PDF_TAG_RECT_GAP_MAX, + ) + grouped_rects = _group_adjacent_rects(rects, max_gap=max_gap) + + line_key = (line["page_index"], line["box_index"], line["line_index"]) + min_x = line_x_cursor.get(line_key, clip.x0 - 1) + + for rect in grouped_rects: + if rect.x0 >= min_x - 0.5: + line_x_cursor[line_key] = rect.x1 + return rect + + chosen = grouped_rects[0] + line_x_cursor[line_key] = chosen.x1 + return chosen + + +def _normalize_line_chars(spans: list[dict]) -> list[dict[str, Any]]: + """ + Normalizes per-character span data into searchable character entries. + + Args: + spans (list[dict]): The span collection to normalize into character entries. + + Returns: + list[dict[str, Any]]: The normalized character entries for the line. + """ + chars: list[dict[str, Any]] = [] + for span in spans: + for char in span.get("chars") or []: + norm_text = normalize("NFKC", str(char.get("c") or "")) + if not norm_text: + continue + bbox = pymupdf.Rect(char["bbox"]) + for norm_char in norm_text: + chars.append({"char": norm_char, "bbox": bbox}) + return chars + + +def _line_chars_from_page(page: pymupdf.Page, line: dict) -> list[dict[str, Any]]: + """ + Extracts character-level geometry for a parsed line from the page text. + + Args: + page (pymupdf.Page): The PDF page being processed. + line (dict): The parsed line metadata being processed. + + Returns: + list[dict[str, Any]]: The character entries extracted from the page. + """ + clip = pymupdf.Rect(line["bbox"]) + raw = page.get_text("rawdict", clip=clip) + target_text = normalize("NFKC", str(line.get("text") or "")).strip() + + best_chars: list[dict[str, Any]] = [] + best_score: tuple[float, float, float] | None = None + + for block in raw.get("blocks") or []: + if block.get("type", 0) != 0: + continue + for raw_line in block.get("lines") or []: + chars = _normalize_line_chars(raw_line.get("spans") or []) + if not chars: + continue + + candidate_rect = pymupdf.Rect(raw_line["bbox"]) + candidate_text = "".join(entry["char"] for entry in chars).strip() + overlap = ( + _rect_vertical_overlap(candidate_rect, clip) + if candidate_rect.intersects(clip) + else 0.0 + ) + text_score = 0.0 + if target_text or candidate_text: + text_score = ( + 0.0 + if target_text == candidate_text + else cer(target_text, candidate_text) + ) + bbox_score = ( + abs(candidate_rect.x0 - clip.x0) + + abs(candidate_rect.y0 - clip.y0) + + abs(candidate_rect.x1 - clip.x1) + + abs(candidate_rect.y1 - clip.y1) + ) / 100.0 + score = (1.0 - overlap, text_score, bbox_score) + if best_score is None or score < best_score: + best_score = score + best_chars = chars + + return best_chars + + +def _line_chars_text(chars: list[dict[str, Any]]) -> str: + """ + Builds the searchable text for a character entry list. + + Args: + chars (list[dict[str, Any]]): The character entry list being processed. + + Returns: + str: The concatenated character text. + """ + return "".join(str(entry.get("char") or "") for entry in chars) + + +def _find_line_char_span( + chars: list[dict[str, Any]], + text: str, + *, + start: int = 0, + raw_text: str | None = None, +) -> tuple[int, int] | None: + """ + Finds the character span for a text fragment inside a line. + + Args: + chars (list[dict[str, Any]]): The character entry list being processed. + text (str): The text value being normalized or searched. + start (int, optional): The preferred start offset for the search. Defaults to 0. + raw_text (str | None, optional): The raw line text used as a fallback search surface. Defaults to None. + + Returns: + tuple[int, int] | None: The start and end character offsets, if found. + """ + if not chars or not text: + return None + + haystack = raw_text if raw_text is not None else _line_chars_text(chars) + pattern = _build_flexible_pattern(text) + + def _search(offset: int) -> tuple[int, int] | None: + """ + Searches for the candidate span from the provided offset. + + Args: + offset (int): The search offset used by the nested helper. + + Returns: + tuple[int, int] | None: The matching span for the current offset, if found. + """ + exact_idx = haystack.find(text, offset) + flexible_span = None + if pattern: + match = re.search(pattern, haystack[offset:]) + if match is not None: + flexible_span = (offset + match.start(), offset + match.end()) + + if exact_idx < 0: + return flexible_span + exact_span = (exact_idx, exact_idx + len(text)) + if flexible_span is None: + return exact_span + return min(exact_span, flexible_span, key=lambda span: span[0]) + + span = _search(start) + if span is None and start > 0: + span = _search(0) + return span + + +def _rect_from_char_slice( + chars: list[dict[str, Any]], + start: int, + end: int, +) -> pymupdf.Rect | None: + """ + Builds a rectangle covering the requested character slice. + + Args: + chars (list[dict[str, Any]]): The character entry list being processed. + start (int): The preferred start offset for the search. + end (int): The `end` value used by this helper. + + Returns: + pymupdf.Rect | None: The rectangle covering the requested character slice. + """ + if not chars: + return None + + slice_start = max(int(start), 0) + slice_end = min(int(end), len(chars)) + if slice_end <= slice_start: + return None + + segment = chars[slice_start:slice_end] + if not segment: + return None + + boxes = [entry["bbox"] for entry in segment if str(entry["char"]).strip()] + if not boxes: + boxes = [entry["bbox"] for entry in segment] + if not boxes: + return None + + rect = pymupdf.Rect(boxes[0]) + for bbox in boxes[1:]: + rect.include_rect(bbox) + return rect diff --git a/aymurai/text/anonymization/pdf/ops.py b/aymurai/text/anonymization/pdf/ops.py new file mode 100644 index 00000000..bdad1d0a --- /dev/null +++ b/aymurai/text/anonymization/pdf/ops.py @@ -0,0 +1,828 @@ +from __future__ import annotations + +from typing import Any + +import pymupdf + +from aymurai.logger import get_logger +from aymurai.text.anonymization.alignment import ( + _label_replacement_start as _label_start, +) +from aymurai.text.anonymization.alignment import ( + _label_replacement_text as _label_surface_text, +) +from aymurai.text.anonymization.pdf.common import ( + PDF_TAG_RECT_GAP_MAX, + PDF_TAG_RECT_INSET, + PDF_TAG_RECT_X_PADDING, + PDF_TAG_RECT_Y_PADDING, + _base14_fontname_for_style, + _default_style, + _entity_style_from_spans, + _find_flexible, + _fit_display_token, + _get_base14_font, + _group_adjacent_rects, + _rect_vertical_overlap, +) +from aymurai.text.anonymization.pdf.layout import ( + _find_line_char_span, + _line_chars_from_page, + _line_chars_text, + _pick_rect_group_for_segment, + _rect_from_char_slice, + _resolve_token, +) +from aymurai.text.anonymization.pdf.widgets import ( + _apply_widget_ops, + _entity_overlaps_widget, + _page_widget_infos, + _prepare_signature_widget_ops, +) + +logger = get_logger(__name__) + +_IMAGE_OVERLAP_THRESHOLD = 0.3 + + +def _padded_rect(rect: pymupdf.Rect, clip: pymupdf.Rect) -> pymupdf.Rect: + """ + Pads a rectangle within the provided clipping bounds. + + Args: + rect (pymupdf.Rect): The rectangle used by the helper. + clip (pymupdf.Rect): The clipping rectangle to constrain the operation. + + Returns: + pymupdf.Rect: The padded rectangle clipped to the provided bounds. + """ + padded = pymupdf.Rect(rect) + padded.x0 = max(clip.x0, padded.x0 - PDF_TAG_RECT_X_PADDING) + padded.y0 = max(clip.y0, padded.y0 - PDF_TAG_RECT_Y_PADDING) + padded.x1 = min(clip.x1, padded.x1 + PDF_TAG_RECT_X_PADDING) + padded.y1 = min(clip.y1, padded.y1 + PDF_TAG_RECT_Y_PADDING) + return padded + + +def _render_rect(rect: pymupdf.Rect) -> pymupdf.Rect: + """ + Builds the token rendering rectangle from the padded canvas rectangle. + + Args: + rect (pymupdf.Rect): The rectangle used by the helper. + + Returns: + pymupdf.Rect: The rectangle used to render the replacement token. + """ + render_rect = pymupdf.Rect(rect) + inset = min(PDF_TAG_RECT_INSET, max(render_rect.height * 0.1, 0.0)) + render_rect.x0 += inset + render_rect.x1 -= inset + if render_rect.x1 <= render_rect.x0: + render_rect = pymupdf.Rect(rect) + return render_rect + + +def _text_redact_rect(rect: pymupdf.Rect) -> pymupdf.Rect: + """ + Builds the redaction rectangle used to remove original text. + + Args: + rect (pymupdf.Rect): The rectangle used by the helper. + + Returns: + pymupdf.Rect: The rectangle used for text redaction. + """ + redact_rect = pymupdf.Rect(rect) + edge_inset = min(0.25, max(redact_rect.width * 0.01, 0.05)) + if redact_rect.width > (2 * edge_inset): + redact_rect.x0 += edge_inset + redact_rect.x1 -= edge_inset + return redact_rect + + +def _build_page_op( + rect: pymupdf.Rect, + line: dict | None, + token: str, + is_image: bool = False, + entity_style: dict[str, Any] | None = None, +) -> dict[str, Any]: + """ + Builds the rendering operation metadata for a matched page segment. + + Args: + rect (pymupdf.Rect): The rectangle used by the helper. + line (dict | None): The parsed line metadata being processed. + token (str): The logical replacement token being processed. + is_image (bool, optional): Whether the operation is intended for image-backed content. Defaults to False. + entity_style (dict[str, Any] | None, optional): The resolved style dictionary for the entity text. Defaults to None. + + Returns: + dict[str, Any]: The rendering operation metadata for the segment. + """ + line_clip = pymupdf.Rect(line["bbox"]) if line else pymupdf.Rect(rect) + canvas_rect = _padded_rect(rect, line_clip) + render_rect = _render_rect(canvas_rect) + style = entity_style or (line or {}).get("style") or _default_style() + base_font_size = float((line or {}).get("font_size") or style.get("size") or 10.0) + + # Always use Base-14 fonts: they carry correct bold/italic weight and + # contain all glyphs needed for tags (<, >, _, digits, letters). + # Subset font buffers extracted from the PDF lack many of these glyphs. + fontname = _base14_fontname_for_style(style) + font_obj = _get_base14_font(style) + + display_token, fitted_size = _fit_display_token( + token, + render_rect, + fontname, + base_font_size, + font_obj=font_obj, + ) + + if not display_token or fitted_size is None: + logger.warning( + "Could not fit PDF token '%s' inside rect=%s", + token, + tuple(round(value, 2) for value in canvas_rect), + ) + + return { + "redact_rect": _text_redact_rect(rect), + "background_rect": canvas_rect, + "canvas_rect": canvas_rect, + "render_rect": render_rect, + "line_rect": line_clip, + "text": display_token, + "logical_token": token, + "fontname": fontname, + "fontsize": fitted_size, + "text_align": pymupdf.TEXT_ALIGN_LEFT, + "text_color": style.get("color") or (0.0, 0.0, 0.0), + "style": style, + } + + +def _image_rects_for_clip( + page: pymupdf.Page, + clip: pymupdf.Rect, +) -> list[pymupdf.Rect]: + """ + Collects image rectangles that overlap the given page region. + + Args: + page (pymupdf.Page): The PDF page being processed. + clip (pymupdf.Rect): The clipping rectangle to constrain the operation. + + Returns: + list[pymupdf.Rect]: The image rectangles that overlap the clip region. + """ + rects: list[pymupdf.Rect] = [] + for img_info in page.get_image_info(): + bbox = img_info.get("bbox") + if bbox is None: + continue + img_rect = pymupdf.Rect(bbox) + if img_rect.intersects(clip) and img_rect.get_area() > 0: + rects.append(img_rect) + return rects + + +def _entity_overlaps_image( + page: pymupdf.Page, + entity_rect: pymupdf.Rect, + image_rects: list[pymupdf.Rect], +) -> pymupdf.Rect | None: + """ + Checks whether an entity rectangle overlaps a detected image. + + Args: + page (pymupdf.Page): The PDF page being processed. + entity_rect (pymupdf.Rect): The rectangle representing the entity on the page. + image_rects (list[pymupdf.Rect]): The image rectangles available for overlap checks. + + Returns: + pymupdf.Rect | None: The overlapping image rectangle, if one exists. + """ + for img_rect in image_rects: + overlap = _rect_vertical_overlap(entity_rect, img_rect) + if overlap >= _IMAGE_OVERLAP_THRESHOLD and entity_rect.intersects(img_rect): + return img_rect + return None + + +def _collect_page_redactions( + doc: pymupdf.Document, + paragraphs: list[dict], + render_context: dict[str, Any] | None, +) -> dict[int, list[dict]]: + """ + Collects text, widget, and signature redaction operations for a document. + + Args: + doc (pymupdf.Document): The PDF document being processed. + paragraphs (list[dict]): The paragraph collection being processed. + render_context (dict[str, Any] | None): The rendering context used to resolve replacement tokens. + + Returns: + tuple[dict[int, list[dict]], dict[int, list[dict]], dict[int, list[dict]]]: The page, text-widget, and signature-widget operations. + """ + page_ops: dict[int, list[dict]] = {} + widget_ops: dict[int, list[dict]] = {} + signature_widget_ops: dict[int, list[dict]] = {} + line_x_cursor: dict[tuple[int, int, int], float] = {} + line_char_cache: dict[tuple[int, int, int], list[dict[str, Any]]] = {} + line_char_text_cache: dict[tuple[int, int, int], str] = {} + line_char_cursor: dict[tuple[int, int, int], int] = {} + + # Pre-compute image rects and widgets per page + page_image_rects: dict[int, list[pymupdf.Rect]] = {} + page_widgets: dict[int, list[dict[str, Any]]] = {} + + for paragraph in paragraphs: + metadata = paragraph.get("metadata") or {} + lines = metadata.get("lines") or [] + if not lines: + continue + + page_index = int(metadata["page_index"]) + page = doc[page_index] + line_text = metadata.get("line_text") or "" + box_clip = pymupdf.Rect(metadata.get("box_bbox") or page.rect) + document = paragraph.get("document") or "" + labels = sorted(paragraph.get("labels") or [], key=_label_start) + search_cursor = 0 + + # Lazy-load image rects and widget infos for this page + if page_index not in page_image_rects: + page_image_rects[page_index] = _image_rects_for_clip(page, page.rect) + if page_index not in page_widgets: + page_widgets[page_index] = _page_widget_infos(page) + + for label in labels: + entity_text = _label_surface_text(label, document).strip() + if not entity_text: + continue + + token = _resolve_token(label, render_context) + + span = _find_flexible(line_text, entity_text, start=search_cursor) + if span is None: + span = _find_flexible(line_text, entity_text, start=0) + if span is None: + # -- Fallback: direct page search -- + fallback_rects = [ + rect + for rect in page.search_for(entity_text, clip=box_clip) + if rect.intersects(box_clip) + ] + + # Check if this is a widget-backed entity before falling back to images + if fallback_rects: + fallback_widget = _entity_overlaps_widget( + fallback_rects[0], + page_widgets[page_index], + ) + if fallback_widget is not None: + if ( + fallback_widget["field_type"] + == pymupdf.PDF_WIDGET_TYPE_TEXT + ): + widget_ops.setdefault(page_index, []).append( + { + "widget_xref": fallback_widget["xref"], + "field_name": fallback_widget["field_name"], + "widget_info": fallback_widget, + "entity_text": entity_text, + "logical_token": token, + } + ) + continue + if ( + fallback_widget["field_type"] + == pymupdf.PDF_WIDGET_TYPE_SIGNATURE + ): + op = _build_page_op( + fallback_rects[0], + lines[0] if lines else None, + token, + entity_style=fallback_widget.get("style") or None, + ) + op["widget_xref"] = fallback_widget["xref"] + op["widget_rect"] = fallback_widget["rect"] + signature_widget_ops.setdefault(page_index, []).append(op) + continue + + # Check if this is an image-based entity + if not fallback_rects: + img_match = _try_image_entity( + page, + entity_text, + box_clip, + page_image_rects[page_index], + ) + if img_match is not None: + op = _build_page_op( + img_match, + lines[0] if lines else None, + token, + is_image=True, + ) + op["image_rect"] = img_match + page_ops.setdefault(page_index, []).append(op) + continue + + if fallback_rects: + grouped_rects = _group_adjacent_rects( + fallback_rects, max_gap=PDF_TAG_RECT_GAP_MAX + ) + fallback_line = lines[0] if lines else None + + # Check if any of these rects overlap an image + for rect in grouped_rects: + img_rect = _entity_overlaps_image( + page, + rect, + page_image_rects[page_index], + ) + op = _build_page_op( + rect, + fallback_line, + token, + is_image=(img_rect is not None), + ) + if img_rect is not None: + op["image_rect"] = img_rect + page_ops.setdefault(page_index, []).append(op) + continue + + logger.warning( + "Could not map label '%s' on page=%s box=%s", + entity_text, + metadata.get("page_number"), + metadata.get("box_index"), + ) + continue + + search_cursor = span[1] + + # Collect line segments this entity spans + segments: list[ + tuple[ + dict, + str, + pymupdf.Rect, + pymupdf.Rect | None, + dict, + dict[str, Any] | None, + ] + ] = [] + for line in lines: + overlap_start = max(span[0], line["start"]) + overlap_end = min(span[1], line["end"]) + if overlap_end <= overlap_start: + continue + + segment_text = line_text[overlap_start:overlap_end].strip() + if not segment_text: + continue + + line_key = ( + line["page_index"], + line["box_index"], + line["line_index"], + ) + line_chars = line_char_cache.get(line_key) + if line_chars is None: + line_chars = _line_chars_from_page(page, line) + line_char_cache[line_key] = line_chars + + line_char_text = line_char_text_cache.get(line_key) + if line_char_text is None: + line_char_text = _line_chars_text(line_chars) + line_char_text_cache[line_key] = line_char_text + + raw_span = _find_line_char_span( + line_chars, + segment_text, + start=line_char_cursor.get(line_key, 0), + raw_text=line_char_text, + ) + rect = None + if raw_span is not None: + line_char_cursor[line_key] = raw_span[1] + rect = _rect_from_char_slice(line_chars, raw_span[0], raw_span[1]) + + if rect is None: + raw_start = ( + overlap_start - line["start"] + int(line.get("strip_offset", 0)) + ) + raw_end = ( + overlap_end - line["start"] + int(line.get("strip_offset", 0)) + ) + rect = _rect_from_char_slice(line_chars, raw_start, raw_end) + if rect is None: + rect = _pick_rect_group_for_segment( + page, + line, + segment_text, + line_x_cursor, + ) + + widget_info = _entity_overlaps_widget( + rect, + page_widgets[page_index], + ) + + # Check for image overlap + img_rect = _entity_overlaps_image( + page, + rect, + page_image_rects[page_index], + ) + + # Determine entity-specific style from the span that + # actually contains this text (not the line's dominant style) + offset_in_line = overlap_start - line["start"] + ent_style = _entity_style_from_spans(line, offset_in_line) + + segments.append( + (line, segment_text, rect, img_rect, ent_style, widget_info) + ) + + if not segments: + continue + + if len(segments) == 1: + # Single-line entity: route widget-backed content through the widget path. + line, _seg_text, rect, img_rect, ent_style, widget_info = segments[0] + if widget_info is not None: + if widget_info["field_type"] == pymupdf.PDF_WIDGET_TYPE_TEXT: + widget_ops.setdefault(page_index, []).append( + { + "widget_xref": widget_info["xref"], + "field_name": widget_info["field_name"], + "widget_info": widget_info, + "entity_text": entity_text, + "logical_token": token, + } + ) + continue + if widget_info["field_type"] == pymupdf.PDF_WIDGET_TYPE_SIGNATURE: + op = _build_page_op( + rect, + line, + token, + entity_style=ent_style, + ) + op["widget_xref"] = widget_info["xref"] + op["widget_rect"] = widget_info["rect"] + signature_widget_ops.setdefault(page_index, []).append(op) + continue + + op = _build_page_op( + rect, + line, + token, + is_image=(img_rect is not None), + entity_style=ent_style, + ) + if img_rect is not None: + op["image_rect"] = img_rect + page_ops.setdefault(page_index, []).append(op) + else: + # Multi-line entity: write the token on the widest segment only; blank the others. + widest_idx = max( + range(len(segments)), + key=lambda i: segments[i][2].width, + ) + any_image = any(seg[3] is not None for seg in segments) + shared_image_rect = next( + (seg[3] for seg in segments if seg[3] is not None), + None, + ) + + signature_widget = None + if all(seg[5] is not None for seg in segments): + widget_xrefs = {int(seg[5]["xref"]) for seg in segments} + widget_types = {int(seg[5]["field_type"]) for seg in segments} + if len(widget_xrefs) == 1 and widget_types == { + pymupdf.PDF_WIDGET_TYPE_SIGNATURE + }: + signature_widget = segments[0][5] + + for seg_idx, ( + seg_line, + _seg_text, + seg_rect, + seg_img, + seg_style, + seg_widget, + ) in enumerate(segments): + if seg_idx == widest_idx: + op = _build_page_op( + seg_rect, + seg_line, + token, + is_image=(any_image and signature_widget is None), + entity_style=seg_style, + ) + if signature_widget is None and shared_image_rect is not None: + op["image_rect"] = shared_image_rect + else: + op = _build_page_op( + seg_rect, + seg_line, + token, + is_image=( + (seg_img is not None) and signature_widget is None + ), + entity_style=seg_style, + ) + op["text"] = None + op["fontsize"] = None + if seg_img is not None and signature_widget is None: + op["image_rect"] = seg_img + + if signature_widget is not None: + op["widget_xref"] = signature_widget["xref"] + op["widget_rect"] = signature_widget["rect"] + signature_widget_ops.setdefault(page_index, []).append(op) + else: + page_ops.setdefault(page_index, []).append(op) + + return page_ops, widget_ops, signature_widget_ops + + +def _try_image_entity( + page: pymupdf.Page, + entity_text: str, + clip: pymupdf.Rect, + image_rects: list[pymupdf.Rect], +) -> pymupdf.Rect | None: + """ + Finds the best image rectangle for an entity when text search fails. + + Args: + page (pymupdf.Page): The PDF page being processed. + entity_text (str): The entity text being mapped. + clip (pymupdf.Rect): The clipping rectangle to constrain the operation. + image_rects (list[pymupdf.Rect]): The image rectangles available for overlap checks. + + Returns: + pymupdf.Rect | None: The best image rectangle for the entity, if found. + """ + if not image_rects: + return None + + # Try unclipped text search — the entity might be rendered as real text + # on top of (or near) an image. + text_hits = page.search_for(entity_text) + if text_hits: + for hit_rect in text_hits: + for img_rect in image_rects: + if hit_rect.intersects(img_rect): + return img_rect + + # Fallback: pick the image whose intersection with *clip* is largest + best: pymupdf.Rect | None = None + best_area = 0.0 + for img_rect in image_rects: + if not img_rect.intersects(clip) or img_rect.get_area() <= 0: + continue + intersection = img_rect & clip + area = intersection.get_area() + if area > best_area: + best_area = area + best = img_rect + + return best + + +def _render_text_op(page: pymupdf.Page, op: dict) -> None: + """ + Renders a single anonymization token back onto a page. + + Args: + page (pymupdf.Page): The PDF page being processed. + op (dict): The operation dictionary being processed. + """ + canvas = pymupdf.Rect(op.get("background_rect") or op["canvas_rect"]) + if not op.get("skip_background_fill"): + page.draw_rect( + canvas, + color=(1, 1, 1), + fill=(1, 1, 1), + width=0, + overlay=True, + ) + + if not op.get("text") or not op.get("fontsize"): + return + + render = op["render_rect"] + line_rect = pymupdf.Rect(op.get("line_rect") or render) + style = op.get("style") or {} + base14_name = _base14_fontname_for_style(style) + font_obj = _get_base14_font(style) + + fontsize = float(op["fontsize"]) + descender = float(style.get("descender") or -0.2) + baseline_y = line_rect.y1 + (descender * fontsize) + baseline_y = min( + max(baseline_y, line_rect.y0 + (fontsize * 0.65)), + line_rect.y1 - 0.1, + ) + + text_width = font_obj.text_length(op["text"], fontsize=fontsize) + x_start = render.x0 + max((render.width - text_width) / 2.0, 0.0) + + try: + page.insert_text( + (x_start, baseline_y), + op["text"], + fontname=base14_name, + fontsize=fontsize, + color=op["text_color"], + overlay=True, + ) + return + except Exception as exc: + logger.debug("insert_text failed for '%s': %s", op["text"], exc) + + try: + tw = pymupdf.TextWriter(page.rect, color=op["text_color"]) + tw.fill_textbox( + render, + op["text"], + font=font_obj, + fontsize=fontsize, + align=op.get("text_align", pymupdf.TEXT_ALIGN_CENTER), + ) + tw.write_text(page, overlay=True) + return + except Exception as exc: + logger.debug("TextWriter failed for '%s': %s", op["text"], exc) + + try: + page.insert_textbox( + render, + op["text"], + fontname=base14_name, + fontsize=fontsize, + color=op["text_color"], + align=op.get("text_align", pymupdf.TEXT_ALIGN_CENTER), + overlay=True, + ) + except Exception as exc: + logger.warning( + "All text insertion methods failed for '%s': %s", + op["text"], + exc, + ) + + +def _page_asset_rect(op: dict[str, Any]) -> pymupdf.Rect | None: + """ + Resolves the asset rectangle associated with a page operation. + + Args: + op (dict[str, Any]): The operation dictionary being processed. + + Returns: + pymupdf.Rect | None: The asset rectangle associated with the operation, if any. + """ + asset_rect = op.get("asset_rect") or op.get("image_rect") + if asset_rect is None: + return None + return pymupdf.Rect(asset_rect) + + +def _partition_page_ops( + page_ops: dict[int, list[dict]], +) -> tuple[dict[int, list[dict]], dict[int, list[dict]]]: + """ + Splits page operations into text-only and asset-backed groups. + + Args: + page_ops (dict[int, list[dict]]): The collected page operations grouped by page index. + + Returns: + tuple[dict[int, list[dict]], dict[int, list[dict]]]: The text-only and asset-backed operations. + """ + text_ops: dict[int, list[dict]] = {} + asset_ops: dict[int, list[dict]] = {} + + for page_idx, ops in page_ops.items(): + for op in ops: + if _page_asset_rect(op) is None: + text_ops.setdefault(page_idx, []).append(op) + else: + asset_ops.setdefault(page_idx, []).append(op) + + return text_ops, asset_ops + + +def _apply_text_redactions( + doc: pymupdf.Document, + text_page_ops: dict[int, list[dict]], +) -> None: + """ + Applies text-only redactions and re-renders replacement tokens. + + Args: + doc (pymupdf.Document): The PDF document being processed. + text_page_ops (dict[int, list[dict]]): The text-only page operations grouped by page index. + """ + for page_idx, ops in text_page_ops.items(): + if not ops: + continue + + page = doc[page_idx] + for op in ops: + page.add_redact_annot( + op["redact_rect"], + text=None, + fill=(1, 1, 1), + cross_out=False, + ) + + page.apply_redactions( + images=pymupdf.PDF_REDACT_IMAGE_NONE, + graphics=pymupdf.PDF_REDACT_LINE_ART_NONE, + text=pymupdf.PDF_REDACT_TEXT_REMOVE, + ) + + for op in ops: + _render_text_op(page, op) + + +def _apply_asset_redactions( + doc: pymupdf.Document, + asset_page_ops: dict[int, list[dict]], +) -> None: + """ + Applies asset-backed redactions and re-renders replacement tokens. + + Args: + doc (pymupdf.Document): The PDF document being processed. + asset_page_ops (dict[int, list[dict]]): The asset-backed page operations grouped by page index. + """ + for page_idx, ops in asset_page_ops.items(): + if not ops: + continue + + page = doc[page_idx] + graphics_mode = pymupdf.PDF_REDACT_LINE_ART_NONE + + for op in ops: + asset_rect = _page_asset_rect(op) + if asset_rect is None: + continue + + page.add_redact_annot( + asset_rect, + text=None, + fill=(1, 1, 1), + cross_out=False, + ) + graphics_mode = max( + graphics_mode, + int(op.get("graphics_mode") or pymupdf.PDF_REDACT_LINE_ART_NONE), + ) + + page.apply_redactions( + images=pymupdf.PDF_REDACT_IMAGE_REMOVE, + graphics=graphics_mode, + text=pymupdf.PDF_REDACT_TEXT_REMOVE, + ) + + for op in ops: + _render_text_op(page, op) + + +def _apply_redactions( + doc: pymupdf.Document, + page_ops: dict[int, list[dict]], + widget_ops: dict[int, list[dict]], + signature_widget_ops: dict[int, list[dict]], +) -> None: + """ + Applies all collected PDF redactions in the correct order. + + Args: + doc (pymupdf.Document): The PDF document being processed. + page_ops (dict[int, list[dict]]): The collected page operations grouped by page index. + widget_ops (dict[int, list[dict]]): The collected text widget operations grouped by page index. + signature_widget_ops (dict[int, list[dict]]): The collected signature widget operations grouped by page index. + """ + _apply_widget_ops(doc, widget_ops) + _prepare_signature_widget_ops(doc, signature_widget_ops) + + text_page_ops, asset_page_ops = _partition_page_ops(page_ops) + for page_idx, ops in signature_widget_ops.items(): + asset_page_ops.setdefault(page_idx, []).extend(ops) + + _apply_text_redactions(doc, text_page_ops) + _apply_asset_redactions(doc, asset_page_ops) diff --git a/aymurai/text/anonymization/pdf/sanitize.py b/aymurai/text/anonymization/pdf/sanitize.py new file mode 100644 index 00000000..408f32bf --- /dev/null +++ b/aymurai/text/anonymization/pdf/sanitize.py @@ -0,0 +1,294 @@ +from __future__ import annotations + +from datetime import datetime, timezone +from typing import Any + +import pymupdf + +from aymurai.logger import get_logger +from aymurai.settings import settings + +logger = get_logger(__name__) + + +def _pdf_metadata_mod_date() -> str: + """ + Builds the PDF metadata modification timestamp in UTC. + + Returns: + str: The PDF-formatted UTC modification timestamp. + """ + timestamp = datetime.now(timezone.utc) + return timestamp.strftime("D:%Y%m%d%H%M%S+00'00'") + + +def _append_cleanup_rect( + cleanup_rects: dict[int, list[pymupdf.Rect]], + page_idx: int, + rect: pymupdf.Rect | tuple[float, float, float, float] | None, +) -> None: + """ + Appends a cleanup rectangle for later document sanitization. + + Args: + cleanup_rects (dict[int, list[pymupdf.Rect]]): The cleanup rectangles grouped by page index. + page_idx (int): The page index associated with the operation. + rect (pymupdf.Rect | tuple[float, float, float, float] | None): The rectangle used by the helper. + """ + if rect is None: + return + + cleanup_rect = pymupdf.Rect(rect) + if cleanup_rect.get_area() <= 0: + return + cleanup_rects.setdefault(page_idx, []).append(cleanup_rect) + + +def _cleanup_rect_for_page_op(op: dict[str, Any]) -> pymupdf.Rect | None: + """ + Builds the cleanup rectangle for a standard page operation. + + Args: + op (dict[str, Any]): The operation dictionary being processed. + + Returns: + pymupdf.Rect | None: The cleanup rectangle for the page operation, if available. + """ + if op.get("image_rect") is not None: + cleanup_rect = pymupdf.Rect(op["image_rect"]) + redact_rect = op.get("redact_rect") + if redact_rect is not None: + cleanup_rect.include_rect(pymupdf.Rect(redact_rect)) + return cleanup_rect + + cleanup_source = ( + op.get("redact_rect") or op.get("background_rect") or op.get("canvas_rect") + ) + if cleanup_source is None: + return None + return pymupdf.Rect(cleanup_source) + + +def _cleanup_rect_for_widget_op(op: dict[str, Any]) -> pymupdf.Rect | None: + """ + Builds the cleanup rectangle for a text widget operation. + + Args: + op (dict[str, Any]): The operation dictionary being processed. + + Returns: + pymupdf.Rect | None: The cleanup rectangle for the widget operation, if available. + """ + widget_info = op.get("widget_info") or {} + widget_rect = widget_info.get("rect") + if widget_rect is None: + return None + return pymupdf.Rect(widget_rect) + + +def _cleanup_rect_for_signature_widget_op(op: dict[str, Any]) -> pymupdf.Rect | None: + """ + Builds the cleanup rectangle for a signature widget operation. + + Args: + op (dict[str, Any]): The operation dictionary being processed. + + Returns: + pymupdf.Rect | None: The cleanup rectangle for the signature widget operation, if available. + """ + widget_rect = op.get("widget_rect") + if widget_rect is not None: + return pymupdf.Rect(widget_rect) + + background_rect = op.get("background_rect") or op.get("canvas_rect") + if background_rect is None: + return None + return pymupdf.Rect(background_rect) + + +def _collect_link_cleanup_rects( + page_ops: dict[int, list[dict]], + widget_ops: dict[int, list[dict]], + signature_widget_ops: dict[int, list[dict]], +) -> dict[int, list[pymupdf.Rect]]: + """ + Collects cleanup rectangles used to prune overlapping links. + + Args: + page_ops (dict[int, list[dict]]): The collected page operations grouped by page index. + widget_ops (dict[int, list[dict]]): The collected text widget operations grouped by page index. + signature_widget_ops (dict[int, list[dict]]): The collected signature widget operations grouped by page index. + + Returns: + dict[int, list[pymupdf.Rect]]: The cleanup rectangles grouped by page index. + """ + cleanup_rects: dict[int, list[pymupdf.Rect]] = {} + + for page_idx, ops in page_ops.items(): + for op in ops: + _append_cleanup_rect(cleanup_rects, page_idx, _cleanup_rect_for_page_op(op)) + + for page_idx, ops in widget_ops.items(): + for op in ops: + _append_cleanup_rect( + cleanup_rects, + page_idx, + _cleanup_rect_for_widget_op(op), + ) + + for page_idx, ops in signature_widget_ops.items(): + for op in ops: + _append_cleanup_rect( + cleanup_rects, + page_idx, + _cleanup_rect_for_signature_widget_op(op), + ) + + return cleanup_rects + + +def _remove_overlapping_page_links( + doc: pymupdf.Document, + cleanup_rects: dict[int, list[pymupdf.Rect]], +) -> None: + """ + Deletes page links that overlap anonymized regions. + + Args: + doc (pymupdf.Document): The PDF document being processed. + cleanup_rects (dict[int, list[pymupdf.Rect]]): The cleanup rectangles grouped by page index. + """ + for page_idx, page_rects in cleanup_rects.items(): + if not page_rects: + continue + + page = doc[page_idx] + for link in list(page.get_links()): + link_rect = link.get("from") + if link_rect is None: + continue + link_rect = pymupdf.Rect(link_rect) + if not any(link_rect.intersects(rect) for rect in page_rects): + continue + try: + page.delete_link(link) + except Exception as exc: + logger.warning( + "Failed to delete PDF link on page=%s rect=%s: %s", + page_idx, + tuple(round(value, 2) for value in link_rect), + exc, + ) + + +def _remove_remaining_annotations(doc: pymupdf.Document) -> None: + """ + Deletes residual page annotations after sanitization. + + Args: + doc (pymupdf.Document): The PDF document being processed. + """ + for page_idx, page in enumerate(doc): + for annot in list(page.annots() or []): + try: + page.delete_annot(annot) + except Exception as exc: + logger.warning( + "Failed to delete residual PDF annotation on page=%s: %s", + page_idx, + exc, + ) + + +def _clear_standard_metadata(doc: pymupdf.Document) -> None: + """ + Clears the standard PDF metadata fields on a document. + + Args: + doc (pymupdf.Document): The PDF document being processed. + """ + doc.set_metadata( + { + "title": "", + "author": "", + "subject": "", + "keywords": "", + "creator": "", + "producer": "", + "creationDate": "", + "modDate": "", + "trapped": "", + } + ) + + +def _apply_aymurai_metadata(doc: pymupdf.Document) -> None: + """ + Applies the configured AymurAI tooling metadata fields to the PDF document. + + Args: + doc (pymupdf.Document): The PDF document being processed. + """ + metadata = dict(doc.metadata or {}) + metadata.update( + { + "title": metadata.get("title") or "", + "author": "", + "subject": metadata.get("subject") or "", + "keywords": metadata.get("keywords") or "", + "creator": settings.ANONYMIZATION_METADATA_CREATOR, + "producer": settings.ANONYMIZATION_METADATA_PRODUCER, + "creationDate": metadata.get("creationDate") or "", + "modDate": _pdf_metadata_mod_date(), + "trapped": metadata.get("trapped") or "", + } + ) + doc.set_metadata(metadata) + + +def _sanitize_document( + doc: pymupdf.Document, + cleanup_rects: dict[int, list[pymupdf.Rect]], +) -> None: + """ + Sanitizes document-level PDF metadata, attachments, and annotations. + + Args: + doc (pymupdf.Document): The PDF document being processed. + cleanup_rects (dict[int, list[pymupdf.Rect]]): The cleanup rectangles grouped by page index. + """ + _remove_overlapping_page_links(doc, cleanup_rects) + doc.scrub( + metadata=True, + xml_metadata=True, + javascript=True, + attached_files=True, + embedded_files=True, + thumbnails=True, + reset_responses=True, + hidden_text=True, + clean_pages=True, + remove_links=False, + reset_fields=False, + redactions=False, + ) + _remove_remaining_annotations(doc) + _clear_standard_metadata(doc) + _apply_aymurai_metadata(doc) + + get_xml_metadata = getattr(doc, "get_xml_metadata", None) + del_xml_metadata = getattr(doc, "del_xml_metadata", None) + if callable(get_xml_metadata) and callable(del_xml_metadata): + try: + xml_metadata = get_xml_metadata() + except Exception as exc: + logger.warning("Failed to read PDF XML metadata after scrub: %s", exc) + else: + if xml_metadata: + try: + del_xml_metadata() + except Exception as exc: + logger.warning( + "Failed to delete residual PDF XML metadata: %s", + exc, + ) diff --git a/aymurai/text/anonymization/pdf/watermark.py b/aymurai/text/anonymization/pdf/watermark.py new file mode 100644 index 00000000..c15d9aef --- /dev/null +++ b/aymurai/text/anonymization/pdf/watermark.py @@ -0,0 +1,522 @@ +from __future__ import annotations + +import os +from functools import lru_cache +from pathlib import Path +from typing import Any + +import pymupdf + +from aymurai.logger import get_logger +from aymurai.settings import settings + +logger = get_logger(__name__) + +WATERMARK_PREFIX_TEXT = "Documento anonimizado por " +WATERMARK_LINK_TEXT = "AymurAI" +WATERMARK_TEXT = f"{WATERMARK_PREFIX_TEXT}{WATERMARK_LINK_TEXT}" +WATERMARK_URL = "https://www.aymurai.info/" +WATERMARK_FONT_SIZE = 10.0 +WATERMARK_MARGIN_X = 24.0 +WATERMARK_BASELINE_MARGIN = 12.0 +WATERMARK_TOP_BASELINE = 22.0 +WATERMARK_RECT_PADDING_X = 4.0 +WATERMARK_RECT_PADDING_Y = 4.0 +WATERMARK_COLLISION_PADDING = 12.0 +WATERMARK_TEXT_COLOR = tuple(channel / 255 for channel in (192, 192, 192)) +WATERMARK_LINK_COLOR = tuple(channel / 255 for channel in (115, 190, 250)) + + +def _candidate_font_paths() -> tuple[list[Path], list[Path]]: + """ + Builds the ordered list of candidate font paths for the PDF watermark. + + Returns: + tuple[list[Path], list[Path]]: The regular and bold watermark font candidates. + """ + override_regular = ( + os.getenv("PDF_WATERMARK_FONT_REGULAR") or settings.PDF_WATERMARK_FONT_REGULAR + ) + override_bold = ( + os.getenv("PDF_WATERMARK_FONT_BOLD") or settings.PDF_WATERMARK_FONT_BOLD + ) + + regular_candidates: list[Path] = [] + bold_candidates: list[Path] = [] + + if override_regular: + regular_candidates.append(Path(override_regular).expanduser()) + if override_bold: + bold_candidates.append(Path(override_bold).expanduser()) + + resource_roots: list[Path] = [] + resources_base = Path(settings.RESOURCES_BASEPATH) + if resources_base.is_absolute(): + resource_roots.append(resources_base) + else: + resource_roots.append((Path("/workspace") / resources_base).resolve()) + resource_roots.append(resources_base) + + font_roots: list[Path] = [] + for root in resource_roots: + font_roots.extend([root / "fonts", root / "fonts" / "archivo"]) + + for root in font_roots: + regular_candidates.extend( + [ + root / "Archivo-Regular.ttf", + root / "Archivo-Regular.otf", + root / "Archivo[wdth,wght].ttf", + root / "Archivo-VariableFont_wdth,wght.ttf", + ] + ) + bold_candidates.extend( + [ + root / "Archivo-Bold.ttf", + root / "Archivo-Bold.otf", + root / "Archivo-BoldItalic.ttf", + root / "Archivo-VariableFont_wdth,wght.ttf", + root / "Archivo[wdth,wght].ttf", + ] + ) + + system_roots = [ + Path("/usr/share/fonts/truetype/archivo"), + Path("/usr/share/fonts/opentype/archivo"), + Path("/usr/local/share/fonts/archivo"), + Path.home() / ".local/share/fonts", + Path.home() / ".local/share/fonts/archivo", + ] + for root in system_roots: + regular_candidates.extend( + [ + root / "Archivo-Regular.ttf", + root / "Archivo-Regular.otf", + root / "Archivo[wdth,wght].ttf", + root / "Archivo-VariableFont_wdth,wght.ttf", + ] + ) + bold_candidates.extend( + [ + root / "Archivo-Bold.ttf", + root / "Archivo-Bold.otf", + root / "Archivo-BoldItalic.ttf", + root / "Archivo-VariableFont_wdth,wght.ttf", + root / "Archivo[wdth,wght].ttf", + ] + ) + + return regular_candidates, bold_candidates + + +def _first_existing_path(paths: list[Path]) -> str | None: + """ + Returns the first existing file path from the provided candidates. + + Args: + paths (list[Path]): The candidate paths to inspect. + + Returns: + str | None: The first existing file path, if one is found. + """ + seen: set[str] = set() + for path in paths: + expanded = path.expanduser() + resolved = str(expanded) + if resolved in seen: + continue + seen.add(resolved) + if expanded.exists() and expanded.is_file(): + return str(expanded) + return None + + +@lru_cache(maxsize=1) +def _watermark_font_paths() -> tuple[str | None, str | None]: + """ + Resolves the font paths used by the PDF watermark. + + Returns: + tuple[str | None, str | None]: The resolved regular and bold watermark font paths. + """ + regular_candidates, bold_candidates = _candidate_font_paths() + regular_path = _first_existing_path(regular_candidates) + bold_path = _first_existing_path(bold_candidates) + if regular_path is None and bold_path is not None: + regular_path = bold_path + if bold_path is None: + bold_path = regular_path + return regular_path, bold_path + + +@lru_cache(maxsize=1) +def _watermark_font_config() -> dict[str, Any]: + """ + Builds the font configuration used to render the PDF watermark. + + Returns: + dict[str, Any]: The watermark font configuration dictionary. + """ + regular_path, bold_path = _watermark_font_paths() + if regular_path: + try: + return { + "text_fontname": "archivo-watermark", + "text_fontfile": regular_path, + "text_font": pymupdf.Font(fontfile=regular_path), + "link_fontname": "archivo-watermark-bold", + "link_fontfile": bold_path or regular_path, + "link_font": pymupdf.Font(fontfile=bold_path or regular_path), + } + except Exception as exc: + logger.warning( + "Could not load Archivo font for PDF watermark, falling back to Base-14 fonts: %s", + exc, + ) + + return { + "text_fontname": "Helvetica", + "text_fontfile": None, + "text_font": pymupdf.Font("Helvetica"), + "link_fontname": "Helvetica-Bold", + "link_fontfile": None, + "link_font": pymupdf.Font("Helvetica-Bold"), + } + + +def _watermark_text_length( + text: str, + *, + font_obj: pymupdf.Font, + fontname: str, + fontsize: float, +) -> float: + """ + Measures the rendered width of watermark text. + + Args: + text (str): The text value being normalized or searched. + font_obj (pymupdf.Font): The font object used for measurement. + fontname (str): The font name to use for measurement or rendering. + fontsize (float): The font size used for measurement or rendering. + + Returns: + float: The rendered width of the watermark text. + """ + try: + return float(font_obj.text_length(text, fontsize=fontsize)) + except Exception: + return float( + pymupdf.get_text_length(text, fontname=fontname, fontsize=fontsize) + ) + + +def _insert_watermark_text( + page: pymupdf.Page, + point: tuple[float, float], + text: str, + *, + fontname: str, + fontsize: float, + color: tuple[float, float, float], + fontfile: str | None = None, +) -> None: + """ + Inserts watermark text onto a page using the resolved font settings. + + Args: + page (pymupdf.Page): The PDF page being processed. + point (tuple[float, float]): The insertion point on the page. + text (str): The text value being normalized or searched. + fontname (str): The font name to use for measurement or rendering. + fontsize (float): The font size used for measurement or rendering. + color (tuple[float, float, float]): The PDF RGB color used to render the text. + fontfile (str | None, optional): The optional font file path to embed for rendering. Defaults to None. + """ + kwargs: dict[str, Any] = { + "fontsize": fontsize, + "fontname": fontname, + "color": color, + "overlay": True, + } + if fontfile: + kwargs["fontfile"] = fontfile + page.insert_text(point, text, **kwargs) + + +def _expanded_rect(rect: pymupdf.Rect, padding: float) -> pymupdf.Rect: + """ + Expands a rectangle by a uniform padding in every direction. + + Args: + rect (pymupdf.Rect): The rectangle to expand. + padding (float): The amount of padding to apply on every side. + + Returns: + pymupdf.Rect: The expanded rectangle. + """ + return pymupdf.Rect( + rect.x0 - padding, + rect.y0 - padding, + rect.x1 + padding, + rect.y1 + padding, + ) + + +def _watermark_corner_order(page_index: int) -> list[str]: + """ + Builds the preferred watermark corner order for a page. + + Args: + page_index (int): The page index being processed. + + Returns: + list[str]: The ordered watermark corner candidates for the page. + """ + if page_index % 2 == 0: + return ["bottom-right", "bottom-left", "top-left", "top-right"] + return ["bottom-left", "top-left", "top-right", "bottom-right"] + + +def _watermark_layout_for_corner( + page: pymupdf.Page, + corner: str, + *, + prefix_width: float, + link_width: float, + total_width: float, +) -> dict[str, Any]: + """ + Builds the watermark geometry for a specific page corner. + + Args: + page (pymupdf.Page): The PDF page being processed. + corner (str): The corner identifier used to position the watermark. + prefix_width (float): The rendered width of the watermark prefix text. + link_width (float): The rendered width of the watermark link text. + total_width (float): The total rendered width of the watermark text. + + Returns: + dict[str, Any]: The watermark layout data for the corner. + """ + if corner.endswith("right"): + x_start = max( + WATERMARK_MARGIN_X, + page.rect.width - total_width - WATERMARK_MARGIN_X, + ) + else: + x_start = WATERMARK_MARGIN_X + + if corner.startswith("bottom"): + baseline_y = page.rect.height - WATERMARK_BASELINE_MARGIN + else: + baseline_y = WATERMARK_TOP_BASELINE + + link_x = x_start + prefix_width + text_top = baseline_y - WATERMARK_FONT_SIZE + banner_rect = pymupdf.Rect( + x_start - WATERMARK_RECT_PADDING_X, + text_top - WATERMARK_RECT_PADDING_Y, + x_start + total_width + WATERMARK_RECT_PADDING_X, + baseline_y + WATERMARK_RECT_PADDING_Y, + ) + link_rect = pymupdf.Rect( + link_x, + text_top, + link_x + link_width, + baseline_y + 2.0, + ) + + return { + "corner": corner, + "x_start": x_start, + "baseline_y": baseline_y, + "link_x": link_x, + "banner_rect": banner_rect, + "link_rect": link_rect, + } + + +def _occupied_page_rects(page: pymupdf.Page) -> list[pymupdf.Rect]: + """ + Collects page rectangles already occupied by visible content. + + Args: + page (pymupdf.Page): The PDF page being processed. + + Returns: + list[pymupdf.Rect]: The occupied rectangles found on the page. + """ + occupied: list[pymupdf.Rect] = [] + + text_data = page.get_text("dict") + for block in text_data.get("blocks", []): + bbox = block.get("bbox") + if bbox is None: + continue + rect = pymupdf.Rect(bbox) + if rect.get_area() <= 0: + continue + occupied.append(_expanded_rect(rect, WATERMARK_COLLISION_PADDING)) + + for drawing in page.get_drawings(): + rect = drawing.get("rect") + if rect is None: + continue + rect = pymupdf.Rect(rect) + if rect.get_area() <= 0: + continue + occupied.append(_expanded_rect(rect, WATERMARK_COLLISION_PADDING)) + + return occupied + + +def _watermark_overlap_score( + banner_rect: pymupdf.Rect, + occupied_rects: list[pymupdf.Rect], +) -> tuple[float, float, int]: + """ + Scores a watermark placement by the amount of page content it overlaps. + + Args: + banner_rect (pymupdf.Rect): The watermark banner rectangle being scored. + occupied_rects (list[pymupdf.Rect]): The occupied page rectangles used for overlap checks. + + Returns: + tuple[float, float, int]: The overlap ratio, overlap area, and overlap count for the placement. + """ + overlap_area = 0.0 + overlap_count = 0 + banner_area = max(banner_rect.get_area(), 1.0) + + for rect in occupied_rects: + if not banner_rect.intersects(rect): + continue + intersection = banner_rect & rect + area = intersection.get_area() + if area <= 0: + continue + overlap_area += area + overlap_count += 1 + + return overlap_area / banner_area, overlap_area, overlap_count + + +def _choose_watermark_layout( + page: pymupdf.Page, + page_index: int, + *, + prefix_width: float, + link_width: float, + total_width: float, +) -> dict[str, Any]: + """ + Selects the watermark placement with the least overlap on a page. + + Args: + page (pymupdf.Page): The PDF page being processed. + page_index (int): The page index being processed. + prefix_width (float): The rendered width of the watermark prefix text. + link_width (float): The rendered width of the watermark link text. + total_width (float): The total rendered width of the watermark text. + + Returns: + dict[str, Any]: The chosen watermark layout data. + """ + occupied_rects = _occupied_page_rects(page) + candidate_layouts = [ + _watermark_layout_for_corner( + page, + corner, + prefix_width=prefix_width, + link_width=link_width, + total_width=total_width, + ) + for corner in _watermark_corner_order(page_index) + ] + + best_layout = candidate_layouts[0] + best_score: tuple[float, float, int] | None = None + + for layout in candidate_layouts: + score = _watermark_overlap_score(layout["banner_rect"], occupied_rects) + if score[0] == 0.0 and score[1] == 0.0: + return layout + if best_score is None or score < best_score: + best_layout = layout + best_score = score + + return best_layout + + +def add_pdf_footer_watermark(doc: pymupdf.Document) -> None: + """ + Adds the anonymization watermark to the least crowded corner of each PDF page. + + Args: + doc (pymupdf.Document): The PDF document being processed. + """ + font_config = _watermark_font_config() + prefix_width = _watermark_text_length( + WATERMARK_PREFIX_TEXT, + font_obj=font_config["text_font"], + fontname=font_config["text_fontname"], + fontsize=WATERMARK_FONT_SIZE, + ) + link_width = _watermark_text_length( + WATERMARK_LINK_TEXT, + font_obj=font_config["link_font"], + fontname=font_config["link_fontname"], + fontsize=WATERMARK_FONT_SIZE, + ) + total_width = prefix_width + link_width + + for page_index, page in enumerate(doc): + layout = _choose_watermark_layout( + page, + page_index, + prefix_width=prefix_width, + link_width=link_width, + total_width=total_width, + ) + baseline_y = layout["baseline_y"] + x_start = layout["x_start"] + link_x = layout["link_x"] + + _insert_watermark_text( + page, + (x_start, baseline_y), + WATERMARK_PREFIX_TEXT, + fontname=font_config["text_fontname"], + fontsize=WATERMARK_FONT_SIZE, + color=WATERMARK_TEXT_COLOR, + fontfile=font_config["text_fontfile"], + ) + _insert_watermark_text( + page, + (link_x, baseline_y), + WATERMARK_LINK_TEXT, + fontname=font_config["link_fontname"], + fontsize=WATERMARK_FONT_SIZE, + color=WATERMARK_LINK_COLOR, + fontfile=font_config["link_fontfile"], + ) + + if layout["corner"].startswith("bottom"): + underline_y = min(page.rect.height - 1.0, baseline_y + 1.0) + else: + underline_y = baseline_y + 1.0 + page.draw_line( + (link_x, underline_y), + (link_x + link_width, underline_y), + color=WATERMARK_LINK_COLOR, + width=0.8, + overlay=True, + ) + page.insert_link( + { + "kind": pymupdf.LINK_URI, + "from": layout["link_rect"], + "uri": WATERMARK_URL, + } + ) diff --git a/aymurai/text/anonymization/pdf/widgets.py b/aymurai/text/anonymization/pdf/widgets.py new file mode 100644 index 00000000..3ea97d7e --- /dev/null +++ b/aymurai/text/anonymization/pdf/widgets.py @@ -0,0 +1,323 @@ +from __future__ import annotations + +from typing import Any + +import pymupdf + +from aymurai.logger import get_logger +from aymurai.text.anonymization.pdf.common import ( + _build_display_token_candidates, + _default_style, + _find_flexible, + _get_base14_font, +) + +logger = get_logger(__name__) + + +def _signature_background_rect( + op: dict[str, Any], + widget_rect: pymupdf.Rect, +) -> pymupdf.Rect: + """ + Builds the background rectangle used for a signature replacement. + + Args: + op (dict[str, Any]): The operation dictionary being processed. + widget_rect (pymupdf.Rect): The rectangle occupied by the widget. + + Returns: + pymupdf.Rect: The background rectangle for the signature replacement. + """ + background = pymupdf.Rect( + op.get("line_rect") or op.get("canvas_rect") or widget_rect + ) + canvas_rect = op.get("canvas_rect") + if canvas_rect is not None: + background.include_rect(pymupdf.Rect(canvas_rect)) + + pad_x = max(background.height * 0.75, 2.0) + pad_y = max(background.height * 0.25, 0.75) + widget_clip = pymupdf.Rect(widget_rect) + + background.x0 = max(widget_clip.x0, background.x0 - pad_x) + background.y0 = max(widget_clip.y0, background.y0 - pad_y) + background.x1 = min(widget_clip.x1, background.x1 + pad_x) + background.y1 = min(widget_clip.y1, background.y1 + pad_y) + return background + + +def _widget_text_color(widget: pymupdf.Widget) -> tuple[float, float, float]: + """ + Extracts the text color configured on a PDF widget. + + Args: + widget (pymupdf.Widget): The widget being processed. + + Returns: + tuple[float, float, float]: The widget text color in PDF RGB components. + """ + values = list(widget.text_color or []) + if not values: + return (0.0, 0.0, 0.0) + if len(values) == 1: + shade = float(values[0]) + return (shade, shade, shade) + if len(values) >= 3: + return tuple(float(value) for value in values[:3]) + return (0.0, 0.0, 0.0) + + +def _style_from_widget(widget: pymupdf.Widget) -> dict[str, Any]: + """ + Builds a text style dictionary from a widget definition. + + Args: + widget (pymupdf.Widget): The widget being processed. + + Returns: + dict[str, Any]: The style dictionary derived from the widget. + """ + return { + "font": str(widget.text_font or ""), + "flags": 0, + "color": _widget_text_color(widget), + "size": float(widget.text_fontsize or 10.0), + "ascender": 0.8, + "descender": -0.2, + } + + +def _page_widget_infos(page: pymupdf.Page) -> list[dict[str, Any]]: + """ + Collects text and signature widget metadata for a page. + + Args: + page (pymupdf.Page): The PDF page being processed. + + Returns: + list[dict[str, Any]]: The widget metadata collected for the page. + """ + infos: list[dict[str, Any]] = [] + for widget in page.widgets() or []: + if widget.field_type not in ( + pymupdf.PDF_WIDGET_TYPE_TEXT, + pymupdf.PDF_WIDGET_TYPE_SIGNATURE, + ): + continue + infos.append( + { + "xref": int(widget.xref), + "field_type": int(widget.field_type), + "field_name": str(widget.field_name or ""), + "field_value": str(widget.field_value or ""), + "rect": pymupdf.Rect(widget.rect), + "style": _style_from_widget(widget), + } + ) + return infos + + +def _entity_overlaps_widget( + entity_rect: pymupdf.Rect, + widget_infos: list[dict[str, Any]], +) -> dict[str, Any] | None: + """ + Finds the widget that most overlaps the given entity rectangle. + + Args: + entity_rect (pymupdf.Rect): The rectangle representing the entity on the page. + widget_infos (list[dict[str, Any]]): The widget metadata available for overlap checks. + + Returns: + dict[str, Any] | None: The best overlapping widget info, if one exists. + """ + best_widget: dict[str, Any] | None = None + best_area = 0.0 + for widget_info in widget_infos: + widget_rect = widget_info["rect"] + if not entity_rect.intersects(widget_rect): + continue + area = (entity_rect & widget_rect).get_area() + if area > best_area: + best_area = area + best_widget = widget_info + return best_widget + + +def _fit_widget_token( + widget_info: dict[str, Any], + current_text: str, + entity_span: tuple[int, int], + token: str, +) -> str: + """ + Finds a token variant that fits inside a widget value. + + Args: + widget_info (dict[str, Any]): The widget metadata being processed. + current_text (str): The current widget text value. + entity_span (tuple[int, int]): The span of the entity inside the widget text. + token (str): The logical replacement token being processed. + + Returns: + str: The token variant that fits in the widget value. + """ + style = widget_info.get("style") or _default_style() + rect = pymupdf.Rect(widget_info["rect"]) + font_obj = _get_base14_font(style) + max_width = max(rect.width - 1.0, 1.0) + + prefix = current_text[: entity_span[0]] + suffix = current_text[entity_span[1] :] + + for candidate in _build_display_token_candidates(token): + candidate_text = f"{prefix}{candidate}{suffix}" + if ( + font_obj.text_length( + candidate_text, fontsize=float(style.get("size") or 10.0) + ) + <= max_width + 0.1 + ): + return candidate + + candidates = _build_display_token_candidates(token) + return candidates[0] if candidates else f"<{token}>" + + +def _apply_widget_ops( + doc: pymupdf.Document, + widget_ops: dict[int, list[dict]], +) -> None: + """ + Applies collected replacements to editable text widgets. + + Args: + doc (pymupdf.Document): The PDF document being processed. + widget_ops (dict[int, list[dict]]): The collected text widget operations grouped by page index. + """ + for page_idx, ops in widget_ops.items(): + if not ops: + continue + + page = doc[page_idx] + widgets = { + int(widget.xref): widget + for widget in (page.widgets() or []) + if widget.field_type == pymupdf.PDF_WIDGET_TYPE_TEXT + } + grouped: dict[int, list[dict]] = {} + for op in ops: + grouped.setdefault(int(op["widget_xref"]), []).append(op) + + for widget_xref, replacements in grouped.items(): + widget = widgets.get(widget_xref) + if widget is None: + logger.warning( + "Could not resolve PDF widget xref=%s on page=%s", + widget_xref, + page_idx, + ) + continue + + current_text = str(widget.field_value or "") + if not current_text: + continue + + search_cursor = 0 + changed = False + for replacement in replacements: + entity_text = replacement["entity_text"] + span = _find_flexible(current_text, entity_text, start=search_cursor) + if span is None: + span = _find_flexible(current_text, entity_text, start=0) + if span is None: + logger.warning( + "Could not map widget label '%s' in widget '%s' on page=%s", + entity_text, + replacement.get("field_name") or widget.field_name, + page_idx, + ) + continue + + token_text = _fit_widget_token( + replacement["widget_info"], + current_text, + span, + replacement["logical_token"], + ) + current_text = ( + f"{current_text[: span[0]]}{token_text}{current_text[span[1] :]}" + ) + search_cursor = span[0] + len(token_text) + changed = True + + if not changed: + continue + + try: + widget.field_value = current_text + widget.update() + except Exception as exc: + logger.warning( + "Failed to update PDF widget '%s' on page=%s: %s", + widget.field_name, + page_idx, + exc, + ) + + +def _prepare_signature_widget_ops( + doc: pymupdf.Document, + signature_widget_ops: dict[int, list[dict]], +) -> None: + """ + Deletes signature widgets and prepares their replacement operations. + + Args: + doc (pymupdf.Document): The PDF document being processed. + signature_widget_ops (dict[int, list[dict]]): The collected signature widget operations grouped by page index. + """ + for page_idx, ops in signature_widget_ops.items(): + if not ops: + continue + + page = doc[page_idx] + widgets = { + int(widget.xref): widget + for widget in (page.widgets() or []) + if widget.field_type == pymupdf.PDF_WIDGET_TYPE_SIGNATURE + } + grouped: dict[int, list[dict]] = {} + for op in ops: + grouped.setdefault(int(op["widget_xref"]), []).append(op) + + for widget_xref, widget_group_ops in grouped.items(): + widget = widgets.get(widget_xref) + widget_rect = pymupdf.Rect( + widget_group_ops[0].get("widget_rect") or (0, 0, 0, 0) + ) + + if widget is not None: + widget_rect = pymupdf.Rect(widget.rect) + try: + page.delete_widget(widget) + except Exception as exc: + logger.warning( + "Failed to delete signature widget xref=%s on page=%s: %s", + widget_xref, + page_idx, + exc, + ) + else: + logger.warning( + "Could not resolve PDF signature widget xref=%s on page=%s", + widget_xref, + page_idx, + ) + + for op in widget_group_ops: + op["widget_rect"] = pymupdf.Rect(widget_rect) + op["asset_rect"] = pymupdf.Rect(widget_rect) + op["graphics_mode"] = pymupdf.PDF_REDACT_LINE_ART_REMOVE_IF_COVERED + op["background_rect"] = _signature_background_rect(op, widget_rect) diff --git a/aymurai/text/extractors/pdf.py b/aymurai/text/extractors/pdf.py index 0e83c30d..c672dfe7 100644 --- a/aymurai/text/extractors/pdf.py +++ b/aymurai/text/extractors/pdf.py @@ -9,27 +9,11 @@ class PdfExtractor(BaseExtractor): extension = "pdf" - def extract(self, path: Path, y_tolerance: float | None = None, **_: Any) -> str: - """ - Extract normalized text from a PDF document. - - Args: - path (Path): Input document path. - y_tolerance (float | None, optional): Maximum vertical gap used to - merge nearby text blocks. If None, it is estimated from the - document. Defaults to None. - **_ (Any): Ignored extra keyword arguments for backward compatibility. - - Returns: - str: Cleaned textual content. - - Raises: - InvalidFile: If the file is unreadable or extraction fails. - """ + def extract(self, path: Path, **_: Any) -> str: file_path = self.ensure_file(path) try: - return pdf_to_text(file_path, y_tolerance=y_tolerance) + return pdf_to_text(file_path) except (OSError, ValueError) as exc: raise InvalidFile(str(exc)) from exc except Exception as exc: diff --git a/aymurai/text/extractors/utils.py b/aymurai/text/extractors/utils.py index 009b562d..8db4c661 100644 --- a/aymurai/text/extractors/utils.py +++ b/aymurai/text/extractors/utils.py @@ -1,12 +1,13 @@ -import statistics +import re import unicodedata import xml.etree.ElementTree as ET import zipfile from pathlib import Path -from typing import Any +from typing import AbstractSet, Any -import numpy as np import pymupdf +import pymupdf.layout # noqa: F401 # activates layout support +import pymupdf4llm import xmltodict from lxml import etree from more_itertools import flatten @@ -18,6 +19,7 @@ ODT_NS = {"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0"} +PDF_SKIP_BOX_CLASSES = frozenset({"picture", "formula", "table"}) def normalize_text(text: str) -> str: @@ -33,109 +35,83 @@ def normalize_text(text: str) -> str: return unicodedata.normalize("NFKC", text) -def _compute_median_margin_between_blocks(pdf_path: str) -> float: +def _clean_pdf_box_text(text: str, box_class: str) -> str: """ - Computes the median vertical margin between text blocks in a PDF. + Clean box-level PDF text while preserving the original layout content. + Args: - pdf_path (str): Path to the PDF file. + text (str): Raw text sliced from a page box. + box_class (str): Box class emitted by ``pymupdf4llm``. + Returns: - float: Median margin between text blocks (in points). + str: Cleaned, normalized box text. """ - margins = [] - - with pymupdf.open(pdf_path) as doc: - for page in doc: - # Extract all text blocks from the page - blocks = page.get_text("blocks") - - # Sort blocks by their top y-coordinate (y0) - blocks_sorted = sorted(blocks, key=lambda b: b[1]) - - # Compute vertical margins between consecutive blocks - for i in range(1, len(blocks_sorted)): - previous_block = blocks_sorted[i - 1] - current_block = blocks_sorted[i] - - # Calculate the vertical margin - previous_y1 = previous_block[3] # Bottom of the previous block - current_y0 = current_block[1] # Top of the current block - margin = current_y0 - previous_y1 + text = normalize_text(text).strip() + if box_class == "footnote": + text = re.sub(r"(?m)^>\s?", "", text) + return text - if margin > 0: # Ignore overlapping blocks - margins.append(margin) - # Compute and return the median margin - if margins: - return statistics.median(margins) - else: - return 0.0 # Return 0 if no margins were found - - -def _extract_and_merge_paragraphs(pdf_path: str, y_tolerance: float = 5) -> list[str]: +def pdf_to_paragraphs( + file_path: Path | str, + *, + include_headers: bool = True, + include_footers: bool = True, + skip_box_classes: AbstractSet[str] = PDF_SKIP_BOX_CLASSES, +) -> list[str]: """ - Extracts and merges paragraphs from a PDF by grouping close text blocks. + Extract paragraph-like layout units from a PDF using PyMuPDF layout parsing. + Args: - pdf_path (str): Path to the PDF file. - y_tolerance (float, optional): Maximum vertical gap (in points) to consider blocks part of the same paragraph. - Defaults to 5. + file_path (Path | str): Path to the PDF document. + include_headers (bool): Whether to keep header boxes. Defaults to True. + include_footers (bool): Whether to keep footer boxes. Defaults to True. + skip_box_classes (AbstractSet[str]): Layout box classes to ignore. Defaults to PDF_SKIP_BOX_CLASSES. + Returns: - list[str]: A list of merged paragraphs as strings. + list[str]: Normalized paragraph strings extracted from the PDF. """ - paragraphs = [] - current_paragraph = [] - last_y1 = None - - with pymupdf.open(pdf_path) as doc: - for page in doc: - # Extract all text blocks from the page - blocks = page.get_text("blocks") - - # Sort blocks by their top y-coordinate (y0) - blocks_sorted = sorted(blocks, key=lambda b: b[1]) - - for block in blocks_sorted: - x0, y0, x1, y1, text, *_ = block - - if last_y1 is not None and (y0 - last_y1) > y_tolerance: - # If the gap between blocks is too large, start a new paragraph - if current_paragraph: - paragraphs.append(" ".join(current_paragraph)) - current_paragraph = [] - - current_paragraph.append(text) - last_y1 = y1 - - if current_paragraph: - paragraphs.append(" ".join(current_paragraph)) - current_paragraph = [] + logger.debug("Extracting layout paragraphs from PDF: %s", file_path) + + with pymupdf.open(str(file_path)) as doc: + chunks = pymupdf4llm.to_text( + doc, + filename=str(file_path), + page_chunks=True, + header=include_headers, + footer=include_footers, + show_progress=False, + force_text=True, + use_ocr=False, + force_ocr=False, + ) + + paragraphs: list[str] = [] + for chunk in chunks: + page_text = chunk.get("text") or "" + for box in chunk.get("page_boxes") or []: + if box.get("class") in skip_box_classes: + continue + + start, stop = box.get("pos", (0, 0)) + text = _clean_pdf_box_text(page_text[start:stop], box.get("class") or "") + if text: + paragraphs.append(text) return paragraphs -def pdf_to_text( - file_path: Path | str, - y_tolerance: float | None = None, -) -> str: +def pdf_to_text(file_path: Path | str) -> str: """ - Extract text from a PDF file and return normalized plain text. + Extract normalized plain text from a PDF using filtered layout boxes. Args: - file_path (Path): Path to the PDF document. - y_tolerance (float, optional): Maximum vertical gap (in points) to consider blocks part of the same paragraph. - If None, it will be computed as the median margin between blocks. Defaults to None. + file_path (Path | str): Path to the PDF document. Returns: str: Cleaned textual content extracted from the PDF. """ - logger.info("Extracting text from PDF: %s", file_path) - - if y_tolerance is None: - y_tolerance = _compute_median_margin_between_blocks(file_path) - - paragraphs = _extract_and_merge_paragraphs(file_path, np.ceil(y_tolerance)) - docu = "\n\n".join(paragraphs) - - return normalize_text(docu) + return "\n\n".join(pdf_to_paragraphs(file_path)) def load_xml_from_docx(path: Path, xmlfile: str = "word/footnotes.xml") -> Any | None: diff --git a/aymurai/text/normalize.py b/aymurai/text/normalize.py index 9027a0d8..4154533b 100644 --- a/aymurai/text/normalize.py +++ b/aymurai/text/normalize.py @@ -2,45 +2,72 @@ import unicodedata -def document_normalize(text: str) -> str: - """Normalize extracted text from documents - * join invalid newlines - * remove continous whitespaces +def _normalize_document_characters(text: str) -> str: + """ + Apply character-level normalization without changing document structure. Args: - text (str): document + text (str): Raw extracted document text. Returns: - str: normalized + str: Character-normalized text. """ - - # normalize character encodings - # text = unicodedata.normalize("NFKD", text) + text = text.replace("\r\n", "\n").replace("\r", "\n") text = unicodedata.normalize("NFKC", text) + text = re.sub(r"(“|”)", '"', text) + text = text.replace("\\/", "/") + text = re.sub(r"[ \t]{2,}", " ", text) + return text + - # remove continous whitespace - text = re.sub(r" {2,}", r" ", text) +def _normalize_paragraph_text(text: str) -> str: + """ + Normalize text inside a single paragraph while preserving paragraph borders. + + Args: + text (str): Paragraph text. + + Returns: + str: Normalized paragraph content. + """ + text = re.sub(r"[ \t]*\n[ \t]*", "\n", text.strip()) # delete newline if NEXT char is: # - lower character or a number - # - punctuanion + # - punctuation text = re.sub(r"\n([a-z0-9;:,\.])", r" \g<1>", text) # delete newline if PREVIOUS char is: # - quote mark - # - punctuanions (except '.' because possible ambiguity) + # - punctuations (except '.' because possible ambiguity) text = re.sub(r"([\w,\"-])\n", r"\g<1> ", text) # cleanup some junk - # - multiple newlines, hyphens - text = re.sub(r"\n{2,}", "\n", text) text = re.sub(r"[-]{2,}", "-", text) text = re.sub(r"\.-", ".", text) + text = re.sub(r" {2,}", " ", text) + return text.strip() - # quotation marks - text = re.sub(r"(“|”)", '"', text) - # scaped slashes - text = text.replace("\/", "/") +def document_normalize(text: str, *, preserve_paragraphs: bool = False) -> str: + """Normalize extracted text from documents. - return text + Args: + text (str): Document text. + preserve_paragraphs (bool): Preserve blank-line paragraph boundaries. Defaults to False. + + Returns: + str: Normalized document text. + """ + text = _normalize_document_characters(text) + + if preserve_paragraphs: + paragraphs = [ + _normalize_paragraph_text(paragraph) + for paragraph in re.split(r"\n\s*\n+", text) + if paragraph.strip() + ] + return "\n\n".join(paragraphs) + + text = _normalize_paragraph_text(text) + return re.sub(r"\n{2,}", "\n", text) diff --git a/docs/es/pipelines/anonymizer/README.md b/docs/es/pipelines/anonymizer/README.md index 2d241616..f7f71857 100644 --- a/docs/es/pipelines/anonymizer/README.md +++ b/docs/es/pipelines/anonymizer/README.md @@ -47,7 +47,7 @@ Fuente editable: [../../../pipelines/anonymizer/pipeline.excalidraw](../../../pi ### Módulos backend relevantes - Router: `aymurai/api/endpoints/routers/anonymizer/anonymizer.py` -- Render/anonymize: `aymurai/text/anonymization/doc_anonymizer.py` +- Render/anonymize: `aymurai/text/anonymization/docx.py` and `aymurai/text/anonymization/pdf.py` - Desambiguación canónica: `aymurai/utils/entity_disambiguation/` ## Persistencia (DB) diff --git a/docs/pipelines/anonymizer/README.md b/docs/pipelines/anonymizer/README.md index 11e864e7..67880ba0 100644 --- a/docs/pipelines/anonymizer/README.md +++ b/docs/pipelines/anonymizer/README.md @@ -47,7 +47,7 @@ Editable source: [pipeline.excalidraw](pipeline.excalidraw) ### Core backend modules - Router: `aymurai/api/endpoints/routers/anonymizer/anonymizer.py` -- Rendering: `aymurai/text/anonymization/doc_anonymizer.py` +- Rendering: `aymurai/text/anonymization/docx.py` and `aymurai/text/anonymization/pdf.py` - Canonical entity mapping: `aymurai/utils/entity_disambiguation/` ## Persistence (DB) diff --git a/notebooks/experiments/pdf-support/06-pymupdf-layout.ipynb b/notebooks/experiments/pdf-support/06-pymupdf-layout.ipynb new file mode 100644 index 00000000..803c8d22 --- /dev/null +++ b/notebooks/experiments/pdf-support/06-pymupdf-layout.ipynb @@ -0,0 +1,253 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "1098eca1", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext rich\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "id": "7e81fbe5", + "metadata": {}, + "source": [ + "# End-to-End PDF Anonymization (PyMuPDF Layout + AymurAI API)\n", + "This notebook builds layout-based paragraphs from the source PDF, runs `/anonymizer/predict` + `/anonymizer/disambiguate`, and compiles an anonymized PDF.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "258fbd18", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import pymupdf\n", + "import requests\n", + "from tqdm.auto import tqdm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcfd985e", + "metadata": {}, + "outputs": [], + "source": [ + "# Change these values to test different documents/environments.\n", + "API_URL = \"http://localhost:8999\"\n", + "SOURCE_PDF = Path(\"./document.pdf\")\n", + "\n", + "OUTPUT_DIR = Path(\"./output\")\n", + "USE_CACHE = False\n", + "\n", + "# Optional: keep as None to rely on backend default policies.\n", + "LABEL_POLICIES = None\n", + "\n", + "# Keep aligned with current anonymizer defaults.\n", + "RENDER_POLICY = {\"suffix_mode\": \"auto\", \"suffix_threshold\": 1}\n", + "\n", + "SOURCE_PDF" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3860b71", + "metadata": {}, + "outputs": [], + "source": [ + "def extract_document_via_api(pdf_path: Path) -> dict:\n", + " with pdf_path.open(\"rb\") as handle:\n", + " response = requests.post(\n", + " f\"{API_URL}/document-extract\",\n", + " files={\"file\": (pdf_path.name, handle, \"application/pdf\")},\n", + " timeout=600,\n", + " )\n", + "\n", + " response.raise_for_status()\n", + " return response.json()\n", + "\n", + "\n", + "def predict_paragraph(text: str, retries: int = 2) -> dict:\n", + " last_error = None\n", + " for attempt in range(retries + 1):\n", + " try:\n", + " response = requests.post(\n", + " f\"{API_URL}/anonymizer/predict\",\n", + " json={\"text\": text},\n", + " params={\"use_cache\": USE_CACHE},\n", + " timeout=600,\n", + " )\n", + " response.raise_for_status()\n", + " return response.json()\n", + " except Exception as exc:\n", + " last_error = exc\n", + " if attempt < retries:\n", + " time.sleep(2)\n", + " else:\n", + " raise last_error\n", + "\n", + " raise RuntimeError(\"Predict request exhausted retries\")\n", + "\n", + "\n", + "def disambiguate(predictions: list[dict]) -> dict:\n", + " payload = {\"paragraphs\": predictions}\n", + " if LABEL_POLICIES is not None:\n", + " payload[\"label_policies\"] = LABEL_POLICIES\n", + "\n", + " response = requests.post(\n", + " f\"{API_URL}/anonymizer/disambiguate\",\n", + " json=payload,\n", + " timeout=600,\n", + " )\n", + " response.raise_for_status()\n", + " return response.json()\n", + "\n", + "\n", + "def compile_pdf(pdf_path: Path, annotations: dict) -> Path:\n", + " payload = {\n", + " \"data\": annotations[\"data\"],\n", + " \"render_policy\": RENDER_POLICY,\n", + " }\n", + " if annotations.get(\"label_policies\") is not None:\n", + " payload[\"label_policies\"] = annotations[\"label_policies\"]\n", + "\n", + " with pdf_path.open(\"rb\") as handle:\n", + " response = requests.post(\n", + " f\"{API_URL}/anonymizer/anonymize-document\",\n", + " data={\"annotations\": json.dumps(payload, ensure_ascii=False)},\n", + " files={\"file\": (pdf_path.name, handle, \"application/pdf\")},\n", + " timeout=1200,\n", + " )\n", + "\n", + " response.raise_for_status()\n", + "\n", + " OUTPUT_DIR.mkdir(parents=True, exist_ok=True)\n", + " output_path = OUTPUT_DIR / f\"{pdf_path.stem}.anonymized.pdf\"\n", + " output_path.write_bytes(response.content)\n", + " return output_path" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0a54485", + "metadata": {}, + "outputs": [], + "source": [ + "document_extract_payload = extract_document_via_api(SOURCE_PDF)\n", + "paragraphs = document_extract_payload[\"document\"]\n", + "\n", + "print(f\"Document ID: {document_extract_payload['document_id']}\")\n", + "print(f\"Paragraphs extracted: {len(paragraphs)}\")\n", + "\n", + "paragraphs[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3beaadee", + "metadata": {}, + "outputs": [], + "source": [ + "predictions = [\n", + " predict_paragraph(paragraph)\n", + " for paragraph in tqdm(paragraphs, desc=\"Predicting paragraphs\")\n", + "]\n", + "total_labels = sum(len(pred.get(\"labels\") or []) for pred in predictions)\n", + "print(f\"Predictions: {len(predictions)} paragraphs, {total_labels} labels\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "682760e0", + "metadata": {}, + "outputs": [], + "source": [ + "disambiguated = disambiguate(predictions)\n", + "total_labels = sum(len(pred.get(\"labels\") or []) for pred in disambiguated[\"data\"])\n", + "print(f\"Disambiguated labels: {total_labels}\")\n", + "disambiguated.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eae3f2c9", + "metadata": {}, + "outputs": [], + "source": [ + "[data for data in disambiguated[\"data\"] if data[\"labels\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "665dde4a", + "metadata": {}, + "outputs": [], + "source": [ + "output_pdf = compile_pdf(SOURCE_PDF, disambiguated)\n", + "print(output_pdf.resolve())\n", + "output_pdf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "715a782a", + "metadata": {}, + "outputs": [], + "source": [ + "with pymupdf.open(str(output_pdf)) as doc:\n", + " watermark_hits = sum(\n", + " len(page.search_for(\"Documento anonimizado por AymurAI\")) for page in doc\n", + " )\n", + " print(f\"Pages: {doc.page_count}\")\n", + " print(f\"Watermark hits: {watermark_hits}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a274809", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aymurai", + "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.10.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/api/routers/anonymizer/test_anonymizer.py b/tests/api/routers/anonymizer/test_anonymizer.py index 54a627e1..e003ad33 100644 --- a/tests/api/routers/anonymizer/test_anonymizer.py +++ b/tests/api/routers/anonymizer/test_anonymizer.py @@ -1,14 +1,304 @@ +import base64 import json +import re import subprocess -from unittest.mock import patch +import sys +from datetime import datetime, timedelta, timezone +from pathlib import Path +from unittest.mock import MagicMock, patch +import pymupdf import pytest +from docx import Document from aymurai.database.schema import AnonymizationParagraph from aymurai.database.utils import text_to_uuid +from aymurai.text.anonymization import DocxAnonymizer, PdfAnonymizer, get_anonymizer +from aymurai.text.anonymization.alignment import index_paragraphs from tests.api.conftest import build_label from tests.api.routers.conftest import build_mock_pipeline +PNG_1X1 = base64.b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a6R8AAAAASUVORK5CYII=" +) +WATERMARK_URL = "https://www.aymurai.info/" + +WINDOWS_PYMUPDF_LAYOUT_XFAIL = pytest.mark.xfail( + sys.platform == "win32", + reason="pymupdf4llm ONNX layout model receives int32 tensors on Windows (expects int64)", + strict=False, +) + + +def _write_pdf(path: Path, configure) -> Path: + doc = pymupdf.open() + page = doc.new_page() + configure(doc, page) + doc.save(path) + doc.close() + return path + + +def _label_dict(text: str, label: str = "PER", **attrs) -> dict: + payload = build_label(label, text).model_dump(mode="json") + payload["attrs"].update(attrs) + return payload + + +def _run_pdf_anonymizer( + tmp_path: Path, + source_path: Path, + document: str, + labels: list[dict], +) -> Path: + output_dir = tmp_path / "out" + output_dir.mkdir(exist_ok=True) + output_path = PdfAnonymizer().anonymize( + {"path": str(source_path)}, + [{"document": document, "labels": labels}], + str(output_dir), + ) + return Path(output_path) + + +@pytest.mark.integration +def test_anonymization_package_exports_and_registry_are_stable(): + assert PdfAnonymizer.__name__ == "PdfAnonymizer" + assert DocxAnonymizer.__name__ == "DocxAnonymizer" + assert isinstance(get_anonymizer("pdf"), PdfAnonymizer) + assert isinstance(get_anonymizer("docx"), DocxAnonymizer) + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_falls_back_from_invalid_alt_offsets(tmp_path): + document = "Ana Perez firmo el escrito" + source_path = _write_pdf( + tmp_path / "invalid-alt.pdf", + lambda _doc, page: page.insert_text((72, 72), document), + ) + labels = [ + _label_dict( + "Ana Perez", + aymurai_alt_start_char=999, + aymurai_alt_end_char=1000, + ) + ] + + output_path = _run_pdf_anonymizer(tmp_path, source_path, document, labels) + + with pymupdf.open(output_path) as output_doc: + page_text = output_doc[0].get_text() + + assert "Ana Perez" not in page_text + assert "" in page_text + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_scrubs_pdf_payloads_and_preserves_safe_links(tmp_path): + document = "Ana Perez presento el escrito" + + def configure(doc: pymupdf.Document, page: pymupdf.Page) -> None: + page.insert_text((72, 72), document) + sensitive_rect = page.search_for("Ana Perez")[0] + page.insert_link( + { + "kind": pymupdf.LINK_URI, + "from": sensitive_rect, + "uri": "https://secret.example", + } + ) + safe_rect = pymupdf.Rect(72, 140, 180, 155) + page.insert_text((72, 150), "Portal publico") + page.insert_link( + { + "kind": pymupdf.LINK_URI, + "from": safe_rect, + "uri": "https://safe.example", + } + ) + page.add_file_annot((220, 72), b"attached secret", "attached.txt") + doc.set_metadata( + { + "title": "Secret title", + "author": "Secret author", + "subject": "Secret subject", + "keywords": "alpha,beta", + "creator": "Secret creator", + "producer": "Secret producer", + } + ) + doc.set_xml_metadata("top-secret") + doc.embfile_add("secret.txt", b"secret bytes", filename="secret.txt") + + source_path = _write_pdf(tmp_path / "metadata.pdf", configure) + labels = [_label_dict("Ana Perez")] + + output_path = _run_pdf_anonymizer(tmp_path, source_path, document, labels) + + with pymupdf.open(output_path) as output_doc: + page = output_doc[0] + link_uris = {link.get("uri") for link in page.get_links()} + + assert output_doc.metadata.get("title") == "" + assert output_doc.metadata.get("subject") == "" + assert output_doc.metadata.get("keywords") == "" + assert output_doc.metadata.get("creationDate") == "" + assert re.fullmatch( + r"D:\d{14}\+00'00'", + output_doc.metadata.get("modDate") or "", + ) + assert output_doc.metadata.get("trapped") == "" + assert output_doc.metadata.get("author") == "" + assert output_doc.metadata.get("creator") == "AymurAI" + assert output_doc.metadata.get("producer") == "AymurAI" + assert not output_doc.get_xml_metadata() + assert output_doc.embfile_names() == [] + assert list(page.annots() or []) == [] + assert "https://secret.example" not in link_uris + assert "https://safe.example" in link_uris + assert WATERMARK_URL in link_uris + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_moves_watermark_away_from_footer_content(tmp_path): + document = "Ana Perez presento el escrito" + footer_rect = pymupdf.Rect(360, 760, 575, 815) + + def configure(_doc: pymupdf.Document, page: pymupdf.Page) -> None: + page.insert_text((72, 72), document) + page.draw_rect(footer_rect, color=(0, 0, 0), fill=(0, 0, 0), overlay=True) + + source_path = _write_pdf(tmp_path / "footer-watermark.pdf", configure) + output_path = _run_pdf_anonymizer( + tmp_path, + source_path, + document, + [_label_dict("Ana Perez")], + ) + + with pymupdf.open(output_path) as output_doc: + page = output_doc[0] + watermark_links = [ + link for link in page.get_links() if link.get("uri") == WATERMARK_URL + ] + + assert len(watermark_links) == 1 + watermark_rect = pymupdf.Rect(watermark_links[0]["from"]) + assert not watermark_rect.intersects(footer_rect) + assert watermark_rect.x1 < footer_rect.x0 + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_removes_image_backed_entities(tmp_path): + source_path = _write_pdf( + tmp_path / "image.pdf", + lambda _doc, page: ( + page.insert_image(pymupdf.Rect(60, 60, 220, 110), stream=PNG_1X1), + page.insert_text((80, 90), "Ana Perez"), + ), + ) + + output_path = _run_pdf_anonymizer( + tmp_path, + source_path, + "Ana Perez", + [_label_dict("Ana Perez")], + ) + + with pymupdf.open(output_path) as output_doc: + page = output_doc[0] + page_text = page.get_text() + + assert page.get_image_info() == [] + assert "Ana Perez" not in page_text + assert "" in page_text + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_removes_signature_widgets_without_restoring_appearance( + tmp_path, +): + def configure(_doc: pymupdf.Document, page: pymupdf.Page) -> None: + page.insert_text((80, 90), "Ana Perez") + widget = pymupdf.Widget() + widget.field_name = "sig_1" + widget.field_type = pymupdf.PDF_WIDGET_TYPE_SIGNATURE + widget.rect = pymupdf.Rect(60, 60, 220, 110) + page.add_widget(widget) + + source_path = _write_pdf(tmp_path / "signature.pdf", configure) + output_path = _run_pdf_anonymizer( + tmp_path, + source_path, + "Ana Perez", + [_label_dict("Ana Perez")], + ) + + with pymupdf.open(output_path) as output_doc: + page = output_doc[0] + page_text = page.get_text() + + assert list(page.widgets() or []) == [] + assert page.get_image_info() == [] + assert "Ana Perez" not in page_text + assert "" in page_text + + +def test_index_paragraphs_reads_docx_xml_as_utf8(tmp_path): + xml_path = tmp_path / "document.xml" + xml_path.write_bytes( + """ + + + Señora — resolución + + +""".encode("utf-8") + ) + + paragraphs = index_paragraphs(str(xml_path)) + + assert len(paragraphs) == 1 + assert paragraphs[0]["plain_text"] == "Señora — resolución" + + +@pytest.mark.integration +def test_docx_anonymizer_sets_aymurai_core_properties(tmp_path): + source_path = tmp_path / "source.docx" + document = Document() + document.add_paragraph("Ana Perez firmo el escrito") + document.core_properties.author = "Sensitive Author" + document.core_properties.last_modified_by = "Sensitive Modifier" + document.save(source_path) + + started_at = datetime.now(timezone.utc).replace(microsecond=0) + + output_path = DocxAnonymizer().anonymize( + {"path": str(source_path)}, + [ + { + "document": "Ana Perez firmo el escrito", + "labels": [_label_dict("Ana Perez")], + } + ], + str(tmp_path / "out"), + ) + + output_document = Document(output_path) + core_properties = output_document.core_properties + assert core_properties.author == "" + assert core_properties.last_modified_by == "AymurAI" + assert core_properties.modified is not None + modified = core_properties.modified + if modified.tzinfo is None: + modified = modified.replace(tzinfo=timezone.utc) + assert started_at <= modified <= datetime.now(timezone.utc) + timedelta(seconds=5) + @pytest.mark.integration @patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") @@ -230,8 +520,8 @@ def test_should_disambiguate_and_persist_paragraphs( ): mock_build_canonical_entities.return_value = [] mock_get_canonical_dates.return_value = [] - mock_map_canonical_entities.side_effect = ( - lambda predictions, canonical_entities: predictions + mock_map_canonical_entities.side_effect = lambda predictions, canonical_entities: ( + predictions ) text = "Ana Pérez denunció en el juzgado." @@ -293,10 +583,53 @@ def test_should_return_validation_when_paragraph_exists(client, db_session): @pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_anonymizer") +def test_should_return_application_pdf_when_pdf_document_is_anonymized( + mock_get_anonymizer, + client, + tmp_path, +): + anonymized_path = _write_pdf( + tmp_path / "output.pdf", + lambda _doc, page: page.insert_text((72, 72), "Anonymized PDF output"), + ) + mock_get_anonymizer.return_value = MagicMock(return_value=str(anonymized_path)) + + annotations = { + "data": [ + { + "document": "Ana Perez presento el escrito", + "labels": [build_label("PER", "Ana Perez").model_dump(mode="json")], + } + ], + "label_policies": {"PER": {"anonymize": True, "disambiguation": "none"}}, + "render_policy": {"suffix_mode": "auto", "suffix_threshold": 1}, + } + + response = client.post( + "/anonymizer/anonymize-document", + data={"annotations": json.dumps(annotations)}, + files={"file": ("sample.pdf", b"%PDF-1.4 fake", "application/pdf")}, + ) + + assert response.status_code == 200 + assert response.headers["content-type"] == "application/pdf" + assert len(response.content) > 0 + + @patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.subprocess.check_output") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_anonymizer") def test_should_anonymize_document_when_annotations_are_valid( - mock_check_output, client + mock_get_anonymizer, mock_check_output, client, tmp_path ): + # Fake anonymizer that writes a dummy docx output + anonymized_path = str(tmp_path / "output.docx") + with open(anonymized_path, "wb") as f: + f.write(b"fake-docx-content") + + mock_anonymizer = MagicMock(return_value=anonymized_path) + mock_get_anonymizer.return_value = mock_anonymizer + def fake_convert(*args, **kwargs): cmd = args[0] source_path = cmd[-1] @@ -320,7 +653,13 @@ def fake_convert(*args, **kwargs): response = client.post( "/anonymizer/anonymize-document", data={"annotations": json.dumps(annotations)}, - files={"file": ("sample.txt", b"input-document", "text/plain")}, + files={ + "file": ( + "sample.docx", + b"input-document", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + }, ) assert response.status_code == 200 @@ -330,9 +669,73 @@ def fake_convert(*args, **kwargs): @pytest.mark.integration @patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.subprocess.check_output") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_anonymizer") +def test_should_exclude_null_alt_attrs_from_anonymize_document_preds( + mock_get_anonymizer, mock_check_output, client, tmp_path +): + anonymized_path = str(tmp_path / "output.docx") + with open(anonymized_path, "wb") as f: + f.write(b"fake-docx-content") + + mock_anonymizer = MagicMock(return_value=anonymized_path) + mock_get_anonymizer.return_value = mock_anonymizer + + def fake_convert(*args, **kwargs): + cmd = args[0] + source_path = cmd[-1] + output_path = source_path.rsplit(".", 1)[0] + ".odt" + with open(output_path, "wb") as output_file: + output_file.write(b"odt-content") + return "ok" + + mock_check_output.side_effect = fake_convert + annotations = { + "data": [ + { + "document": "Ana Perez denuncio en el juzgado.", + "labels": [build_label("PER", "Ana Perez").model_dump(mode="json")], + } + ], + "label_policies": {"PER": {"anonymize": True, "disambiguation": "fuzzy"}}, + "render_policy": {"suffix_mode": "auto", "suffix_threshold": 1}, + } + + response = client.post( + "/anonymizer/anonymize-document", + data={"annotations": json.dumps(annotations)}, + files={ + "file": ( + "sample.docx", + b"input-document", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + }, + ) + + assert response.status_code == 200 + preds = mock_anonymizer.call_args[0][1] + assert preds[0]["labels"][0]["text"] == "Ana Perez" + + attrs = preds[0]["labels"][0]["attrs"] + assert "aymurai_alt_text" not in attrs + assert "aymurai_alt_start_char" not in attrs + assert "aymurai_alt_end_char" not in attrs + + +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.subprocess.check_output") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_anonymizer") def test_should_return_500_when_anonymize_document_conversion_fails( - mock_check_output, client + mock_get_anonymizer, mock_check_output, client, tmp_path ): + # Fake anonymizer that writes a dummy output + anonymized_path = str(tmp_path / "output.docx") + with open(anonymized_path, "wb") as f: + f.write(b"fake-docx-content") + + mock_anonymizer = MagicMock(return_value=anonymized_path) + mock_get_anonymizer.return_value = mock_anonymizer + mock_check_output.side_effect = subprocess.CalledProcessError( 1, ["libreoffice"], @@ -347,7 +750,13 @@ def test_should_return_500_when_anonymize_document_conversion_fails( response = client.post( "/anonymizer/anonymize-document", data={"annotations": json.dumps(annotations)}, - files={"file": ("sample.txt", b"input-document", "text/plain")}, + files={ + "file": ( + "sample.docx", + b"input-document", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + }, ) assert response.status_code == 500 diff --git a/tests/api/routers/misc/test_document_extract.py b/tests/api/routers/misc/test_document_extract.py index 6a67fdd6..124c4b9c 100644 --- a/tests/api/routers/misc/test_document_extract.py +++ b/tests/api/routers/misc/test_document_extract.py @@ -1,5 +1,6 @@ import concurrent.futures import io +import sys from unittest.mock import patch import pytest @@ -74,6 +75,11 @@ def test_should_extract_real_text_from_sample_docx_without_mocking(client): @pytest.mark.integration @pytest.mark.slow +@pytest.mark.xfail( + sys.platform == "win32", + reason="pymupdf4llm ONNX layout model receives int32 tensors on Windows (expects int64)", + strict=False, +) def test_should_extract_real_text_from_pdf_without_mocking(client): """Test that a real PDF upload is extracted without mocking.""" expected_paragraphs = [ diff --git a/tests/api/routers/test_pipeline_flows.py b/tests/api/routers/test_pipeline_flows.py index 3df22aaa..8d53952d 100644 --- a/tests/api/routers/test_pipeline_flows.py +++ b/tests/api/routers/test_pipeline_flows.py @@ -1,9 +1,11 @@ +import io import json import shutil import uuid -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest +from docx import Document as DocxDocument from aymurai.database.schema import DataPublicDocumentParagraph from tests.api.routers.conftest import build_mock_pipeline @@ -20,6 +22,7 @@ def _fake_libreoffice_convert(*args, **kwargs): @pytest.mark.integration @patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.subprocess.check_output") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_anonymizer") @patch( "aymurai.api.endpoints.routers.anonymizer.anonymizer.map_canonical_entities_ner_preds" ) @@ -33,8 +36,10 @@ def test_should_run_anonymizer_flow_end_to_end( mock_build_canonical_entities, mock_get_canonical_dates, mock_map_canonical_entities, + mock_get_anonymizer, mock_check_output, client, + tmp_path, ): mock_extract.return_value = "Ana Pérez denunció.\nJuan Soto declaró." mock_load_pipeline.return_value = build_mock_pipeline() @@ -43,6 +48,12 @@ def test_should_run_anonymizer_flow_end_to_end( mock_map_canonical_entities.side_effect = lambda predictions, canonical_entities: ( predictions ) + + anonymized_path = str(tmp_path / "output.docx") + with open(anonymized_path, "wb") as f: + f.write(b"fake-docx-content") + mock_anonymizer = MagicMock(return_value=anonymized_path) + mock_get_anonymizer.return_value = mock_anonymizer mock_check_output.side_effect = _fake_libreoffice_convert extract_response = client.post( @@ -81,7 +92,13 @@ def test_should_run_anonymizer_flow_end_to_end( compile_response = client.post( "/anonymizer/anonymize-document", data={"annotations": json.dumps(annotations)}, - files={"file": ("sample.txt", b"doc-bytes", "text/plain")}, + files={ + "file": ( + "sample.docx", + b"doc-bytes", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + }, ) assert compile_response.status_code == 200 assert compile_response.headers["content-type"] == "application/octet-stream" @@ -162,10 +179,22 @@ def test_should_compile_anonymized_document_with_real_libreoffice_when_available "render_policy": {"suffix_mode": "auto", "suffix_threshold": 1}, } + doc = DocxDocument() + doc.add_paragraph("Texto base para anonimizar.") + buf = io.BytesIO() + doc.save(buf) + docx_bytes = buf.getvalue() + response = client.post( "/anonymizer/anonymize-document", data={"annotations": json.dumps(annotations)}, - files={"file": ("sample.txt", b"input-document", "text/plain")}, + files={ + "file": ( + "sample.docx", + docx_bytes, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ) + }, ) assert response.status_code == 200 From 3ad788b4cebd5ca4b50ca0d929d0cc6bebf5870d Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 20 Apr 2026 18:49:44 +0000 Subject: [PATCH 093/101] =?UTF-8?q?=F0=9F=90=9B=20Remove=20unnecessary=20-?= =?UTF-8?q?-extra=20runtime=20flag=20from=20uv=20sync=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index 560e1290..bd492f09 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -19,7 +19,7 @@ WORKDIR /app RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ - uv sync --frozen --no-install-project --extra runtime + uv sync --frozen --no-install-project # Copy application code COPY aymurai /app/aymurai From 87c78922631392cb3f0abc7e528a36fbfe34a10b Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Mon, 20 Apr 2026 20:41:02 -0300 Subject: [PATCH 094/101] =?UTF-8?q?=F0=9F=90=9B=20Date=20formatter=20bug?= =?UTF-8?q?=20fixed=20for=20canonical=20entities=20generation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routers/anonymizer/anonymizer.py | 4 + .../entity_disambiguation/date_formatter.py | 16 +++- .../10-anonymize-document-render-policy.ipynb | 87 ++++++++++++++++--- 3 files changed, 93 insertions(+), 14 deletions(-) diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 6a48c33b..c6a2f39d 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -422,9 +422,13 @@ async def anonymizer_disambiguate( else [] ) + logger.info("canonical entities detected were: %s", canonical_entities) + if "FECHA" in fuzzy_labels: canonical_entities += get_canonical_dates(labels) + logger.info("canonical entities after adding dates: %s", canonical_entities) + logger.info( "fuzzy clustering produced %d canonical entities", len(canonical_entities) ) diff --git a/aymurai/utils/entity_disambiguation/date_formatter.py b/aymurai/utils/entity_disambiguation/date_formatter.py index 92e38aea..23196409 100644 --- a/aymurai/utils/entity_disambiguation/date_formatter.py +++ b/aymurai/utils/entity_disambiguation/date_formatter.py @@ -1,5 +1,6 @@ from aymurai.meta.api_interfaces import DocLabel from aymurai.meta.entities import CanonicalEntity +import uuid def get_canonical_dates(labels: list[DocLabel]) -> list[CanonicalEntity]: @@ -20,14 +21,23 @@ def get_canonical_dates(labels: list[DocLabel]) -> list[CanonicalEntity]: continue raw_date = label.attrs.aymurai_alt_text or label.text - norm_date = ( max(label.attrs.aymurai_label_subclass) if label.attrs.aymurai_label_subclass else None ) - day_month_key = norm_date[:5] if norm_date is not None else norm_date + if norm_date: + parts = norm_date.split("/") + day = parts[0] + month = parts[1] + year = parts[2] if len(parts) > 2 else "1900" + if year == "1900": + day_month_key = f"{day}/{month}" + else: + day_month_key = f"{day}/{month}/{year}" + else: + day_month_key = uuid.uuid4() if day_month_key not in groups: groups[day_month_key] = CanonicalEntity( @@ -37,7 +47,7 @@ def get_canonical_dates(labels: list[DocLabel]) -> list[CanonicalEntity]: attributes={}, ) - if day_month_key in groups and raw_date not in groups[day_month_key].aliases: + if raw_date not in groups[day_month_key].aliases: groups[day_month_key].aliases.append(raw_date) label.attrs.canonical_entity_id = groups[day_month_key].entity_id diff --git a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb index a9949050..c072066f 100644 --- a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb +++ b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb @@ -64,7 +64,8 @@ "\n", "print(f\"Found {len(documents)} documents\")\n", "\n", - "doc_path = documents[1]" + "doc_path = documents[14]\n", + "print(f\"Processing document: {doc_path}\")" ] }, { @@ -245,7 +246,11 @@ "source": [ "# Function to make inference using the API\n", "def get_predictions(sample: str) -> dict:\n", - " response = requests.post(url=f\"{API_URL}/anonymizer/predict\", json={\"text\": sample}, params={\"use_cache\": False})\n", + " response = requests.post(\n", + " url=f\"{API_URL}/anonymizer/predict\",\n", + " json={\"text\": sample},\n", + " params={\"use_cache\": False},\n", + " )\n", " response.raise_for_status()\n", " return response.json()" ] @@ -295,6 +300,12 @@ " }\n", " )\n", "\n", + " anonymize_labels = []\n", + " for item in disambiguated[\"data\"]:\n", + " for label in item.get(\"labels\", []):\n", + " if label:\n", + " anonymize_labels.append(label)\n", + "\n", " with open(doc_path, \"rb\") as file:\n", " files = {\"file\": file}\n", "\n", @@ -315,7 +326,7 @@ " with open(out_path, \"wb\") as file:\n", " file.write(response.content)\n", "\n", - " return disambiguated, out_path" + " return disambiguated, out_path, anonymize_labels" ] }, { @@ -331,14 +342,34 @@ "\n", "# 1) everything fuzzy\n", "label_policies_all_fuzzy = {\n", - " \"PER\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": True},\n", - " \"DNI\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"LOC\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"DIRECCION\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", - " \"FECHA\": {\"disambiguation\": \"fuzzy\", \"anonymize\": True, \"use_subclass_when_available\": False},\n", + " \"PER\": {\n", + " \"disambiguation\": \"fuzzy\",\n", + " \"anonymize\": True,\n", + " \"use_subclass_when_available\": True,\n", + " },\n", + " \"DNI\": {\n", + " \"disambiguation\": \"fuzzy\",\n", + " \"anonymize\": True,\n", + " \"use_subclass_when_available\": False,\n", + " },\n", + " \"LOC\": {\n", + " \"disambiguation\": \"fuzzy\",\n", + " \"anonymize\": True,\n", + " \"use_subclass_when_available\": False,\n", + " },\n", + " \"DIRECCION\": {\n", + " \"disambiguation\": \"fuzzy\",\n", + " \"anonymize\": True,\n", + " \"use_subclass_when_available\": False,\n", + " },\n", + " \"FECHA\": {\n", + " \"disambiguation\": \"fuzzy\",\n", + " \"anonymize\": True,\n", + " \"use_subclass_when_available\": False,\n", + " },\n", "}\n", - "disambiguated_labels_fuzzy, out_all_fuzzy = disambiguate_and_export(\n", - " \"all-fuzzy\", label_policies_all_fuzzy, render_policy\n", + "disambiguated_labels_fuzzy, out_all_fuzzy, anonymize_labels_fuzzy = (\n", + " disambiguate_and_export(\"all-fuzzy\", label_policies_all_fuzzy, render_policy)\n", ")\n", "\n", "# # 2) FECHAs excluded\n", @@ -363,7 +394,41 @@ "metadata": {}, "outputs": [], "source": [ - "disambiguated_labels_fuzzy" + "anonymize_labels_fuzzy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import defaultdict\n", + "from typing import Any\n", + "\n", + "\n", + "def group_alt_texts_by_entity(data: list[dict[str, Any]]) -> dict[str, list[str]]:\n", + " grouped_data = defaultdict(list)\n", + "\n", + " for entry in data:\n", + " attrs = entry.get(\"attrs\", {})\n", + " entity_id = attrs.get(\"canonical_entity_id\")\n", + " alt_text = attrs.get(\"aymurai_alt_text\")\n", + " subclass = attrs.get(\"aymurai_label_subclass\")\n", + " if entity_id and alt_text:\n", + " grouped_data[entity_id].append((alt_text, subclass))\n", + "\n", + " return dict(grouped_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result = group_alt_texts_by_entity(anonymize_labels_fuzzy)\n", + "print(json.dumps(result, indent=4, ensure_ascii=False))" ] } ], From a485b083b0a69afdc8230cda516b11051aa103c2 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 4 May 2026 13:59:29 +0000 Subject: [PATCH 095/101] =?UTF-8?q?=F0=9F=90=9B=20Fix=20duplicate=20DocLab?= =?UTF-8?q?el=20handling=20in=20anonymization=20and=20serialization=20proc?= =?UTF-8?q?esses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routers/anonymizer/anonymizer.py | 42 +++++++++++++++++-- aymurai/api/main.py | 17 ++++++++ .../database/crud/anonymization/paragraph.py | 17 +++++++- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index c6a2f39d..6bc7b494 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -2,6 +2,7 @@ import os import subprocess import tempfile +from collections.abc import Iterable from threading import Lock import torch @@ -68,8 +69,10 @@ def _entities_to_doclabels(entities: list[dict]) -> list[DocLabel]: for ent in entities: try: - doclabels.append( - DocLabel.model_validate( + if isinstance(ent, DocLabel): + doclabel = ent + else: + doclabel = DocLabel.model_validate( { "text": ent.get("text", ""), "start_char": ent.get("start_char"), @@ -77,11 +80,41 @@ def _entities_to_doclabels(entities: list[dict]) -> list[DocLabel]: "attrs": ent.get("attrs", {}), } ) - ) + doclabels.append(doclabel) except Exception as exc: # keep going if a single entity is malformed logger.warning(f"Skipping invalid entity for DocLabel: {exc}") - return doclabels + return _dedupe_doclabels(doclabels) + + +def _dedupe_doclabels(labels: Iterable[DocLabel]) -> list[DocLabel]: + """ + Remove exact duplicate labels while preserving first-seen order. + + Args: + labels (Iterable[DocLabel]): An iterable of DocLabel objects, + potentially containing duplicates. + + Returns: + list[DocLabel]: A list of DocLabel objects with duplicates removed, + preserving the order of first occurrence. + """ + deduped: list[DocLabel] = [] + seen: set[str] = set() + + for label in labels: + key = json.dumps( + label.model_dump(mode="json", exclude_none=True), + sort_keys=True, + separators=(",", ":"), + ) + if key in seen: + continue + + seen.add(key) + deduped.append(label) + + return deduped def _merge_label_policies( @@ -444,6 +477,7 @@ async def anonymizer_disambiguate( ) for document in predictions: + document.labels = _dedupe_doclabels(document.labels or []) for label in document.labels or []: label.attrs.aymurai_disambiguation = effective_disambiguation_by_label.get( label.attrs.aymurai_label, "fuzzy" diff --git a/aymurai/api/main.py b/aymurai/api/main.py index b4ef93d6..4bfd9370 100644 --- a/aymurai/api/main.py +++ b/aymurai/api/main.py @@ -8,6 +8,7 @@ from fastapi import FastAPI, Request from fastapi.responses import RedirectResponse from fastapi.middleware.cors import CORSMiddleware +from starlette.formparsers import MultiPartParser from aymurai.api import core from aymurai.logger import get_logger @@ -27,6 +28,22 @@ RESOURCES_BASEPATH = settings.RESOURCES_BASEPATH +MULTIPART_MAX_PART_SIZE = 10 * 1024 * 1024 # 10 MB + + +def _set_kwdefault(func, name: str, value: int) -> None: + kwdefaults = getattr(func, "__kwdefaults__", None) + if kwdefaults and name in kwdefaults: + kwdefaults[name] = value + + +# FastAPI parses Form(...) before endpoint execution. Starlette defaults each +# non-file multipart field to 1MB, which is too small for annotations JSON. +MultiPartParser.max_part_size = MULTIPART_MAX_PART_SIZE +_set_kwdefault(MultiPartParser.__init__, "max_part_size", MULTIPART_MAX_PART_SIZE) +_set_kwdefault(Request.form, "max_part_size", MULTIPART_MAX_PART_SIZE) +_set_kwdefault(Request._get_form, "max_part_size", MULTIPART_MAX_PART_SIZE) + @asynccontextmanager async def lifespan(app: FastAPI): diff --git a/aymurai/database/crud/anonymization/paragraph.py b/aymurai/database/crud/anonymization/paragraph.py index 17f826b8..cf1e30e5 100644 --- a/aymurai/database/crud/anonymization/paragraph.py +++ b/aymurai/database/crud/anonymization/paragraph.py @@ -1,3 +1,4 @@ +import json import uuid from pydantic import TypeAdapter @@ -11,7 +12,6 @@ from aymurai.database.utils import text_to_uuid from aymurai.meta.api_interfaces import DocLabel - _DOC_LABELS_ADAPTER = TypeAdapter(list[DocLabel]) @@ -27,7 +27,20 @@ def _serialize_doclabels(value: list[DocLabel] | None): """ if value is None: return None - return _DOC_LABELS_ADAPTER.dump_python(value, mode="json", exclude_none=True) + + labels = _DOC_LABELS_ADAPTER.dump_python(value, mode="json", exclude_none=True) + deduped = [] + seen = set() + + for label in labels: + key = json.dumps(label, sort_keys=True, separators=(",", ":")) + if key in seen: + continue + + seen.add(key) + deduped.append(label) + + return deduped def _normalize_paragraph_payload(payload: dict) -> dict: From 64aa11519a63b4d01bc7c6a7ac3f258a4bb7b308 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Mon, 4 May 2026 13:59:59 +0000 Subject: [PATCH 096/101] =?UTF-8?q?=E2=9C=85=20Add=20tests=20to=20deduplic?= =?UTF-8?q?ate=20duplicate=20labels=20in=20cached=20predictions=20and=20di?= =?UTF-8?q?sambiguation=20processes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/routers/anonymizer/test_anonymizer.py | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/tests/api/routers/anonymizer/test_anonymizer.py b/tests/api/routers/anonymizer/test_anonymizer.py index e003ad33..8225e29d 100644 --- a/tests/api/routers/anonymizer/test_anonymizer.py +++ b/tests/api/routers/anonymizer/test_anonymizer.py @@ -505,6 +505,46 @@ def test_should_isolate_cache_when_different_texts( assert data1["labels"] != data2["labels"] +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_dedupe_duplicate_labels_when_returning_cached_prediction( + mock_load_pipeline, client, db_session +): + text = "EL SEÑOR JUEZ, doctor Tarte, señaló :" + label = build_label("PER", "Tarte").model_dump(mode="json") + label.update({"start_char": 22, "end_char": 27}) + label["attrs"].update( + { + "aymurai_alt_text": "Tarte", + "aymurai_alt_start_char": 22, + "aymurai_alt_end_char": 27, + "aymurai_disambiguation": "fuzzy", + "aymurai_anonymize": True, + } + ) + + db_session.add( + AnonymizationParagraph( + id=text_to_uuid(text), + text=text, + prediction=[label, label], + ) + ) + db_session.commit() + + response = client.post( + "/anonymizer/predict", + json={"text": text}, + params={"use_cache": True}, + ) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == text + assert data["labels"] == [label] + mock_load_pipeline.assert_not_called() + + @pytest.mark.integration @patch( "aymurai.api.endpoints.routers.anonymizer.anonymizer.map_canonical_entities_ner_preds" @@ -552,6 +592,66 @@ def test_should_disambiguate_and_persist_paragraphs( assert stored.prediction[0]["text"] == "Ana Pérez" +@pytest.mark.integration +@patch( + "aymurai.api.endpoints.routers.anonymizer.anonymizer.map_canonical_entities_ner_preds" +) +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.get_canonical_dates") +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.build_canonical_entities") +def test_should_dedupe_duplicate_labels_when_disambiguating_and_persisting( + mock_build_canonical_entities, + mock_get_canonical_dates, + mock_map_canonical_entities, + client, + db_session, +): + mock_build_canonical_entities.return_value = [] + mock_get_canonical_dates.return_value = [] + mock_map_canonical_entities.side_effect = lambda predictions, canonical_entities: ( + predictions + ) + + text = "EL SEÑOR JUEZ, doctor Tarte, señaló :" + label = build_label("PER", "Tarte").model_dump(mode="json") + label.update({"start_char": 22, "end_char": 27}) + label["attrs"].update( + { + "aymurai_alt_text": "Tarte", + "aymurai_alt_start_char": 22, + "aymurai_alt_end_char": 27, + } + ) + body = { + "paragraphs": [{"document": text, "labels": [label, label]}], + "label_policies": { + "PER": {"anonymize": True, "disambiguation": "none"}, + }, + } + + response = client.post("/anonymizer/disambiguate", json=body) + + assert response.status_code == 200 + labels = response.json()["data"][0]["labels"] + assert labels == [ + { + **label, + "attrs": { + **label["attrs"], + "aymurai_disambiguation": "none", + "aymurai_anonymize": True, + }, + } + ] + + stored = db_session.get(AnonymizationParagraph, text_to_uuid(text)) + assert stored is not None + assert stored.prediction is not None + assert len(stored.prediction) == 1 + assert stored.prediction[0]["text"] == "Tarte" + assert stored.prediction[0]["attrs"]["aymurai_disambiguation"] == "none" + assert stored.prediction[0]["attrs"]["aymurai_anonymize"] is True + + @pytest.mark.integration def test_should_return_null_validation_when_paragraph_not_found(client): response = client.post( From 1860a6c382b2ff9f39a7ba7c17e90cf2a2da301c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Mon, 25 May 2026 10:49:12 -0300 Subject: [PATCH 097/101] =?UTF-8?q?=F0=9F=90=9B=20Fix=20handling=20of=20no?= =?UTF-8?q?n-alphanumeric=20entities=20by=20returning=20None=20for=20empty?= =?UTF-8?q?=20cleaned=20text=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transforms/anonymization_postprocess/core.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/aymurai/transforms/anonymization_postprocess/core.py b/aymurai/transforms/anonymization_postprocess/core.py index 977fb109..bd596d29 100644 --- a/aymurai/transforms/anonymization_postprocess/core.py +++ b/aymurai/transforms/anonymization_postprocess/core.py @@ -1,10 +1,9 @@ import re from copy import deepcopy -from string import punctuation +from aymurai.meta.pipeline_interfaces import Transform from aymurai.meta.types import DataItem from aymurai.utils.misc import get_element -from aymurai.meta.pipeline_interfaces import Transform class AnonymizationEntityCleaner(Transform): @@ -45,6 +44,9 @@ def process(self, ent: dict) -> dict: # Clean the text cleaned_text = pattern.sub("", original_text) + if not cleaned_text: + return None + # Update the entity's alt text and indices ent["attrs"]["aymurai_alt_text"] = cleaned_text ent["attrs"]["aymurai_alt_start_char"] = start_char + leading_chars_removed @@ -61,11 +63,11 @@ def __call__(self, item: DataItem) -> DataItem: DataItem: processed item """ item = deepcopy(item) - ents = get_element(item, [self.field, "entities"]) or [] - # Filter out predictions that are punctuation marks only - ents = [ent for ent in ents if ent["text"] not in punctuation] - ents = [self.process(ent) for ent in ents] + # Filter out predictions with empty alt text and update the rest + item[self.field]["entities"] = [ + out for ent in ents if (out := self.process(ent)) is not None + ] return item From 44ce0ad598c4ccd300ac62134954c33f2d7e3eba Mon Sep 17 00:00:00 2001 From: jansaldo Date: Tue, 26 May 2026 16:32:31 +0000 Subject: [PATCH 098/101] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20default=20timeout?= =?UTF-8?q?=20value=20in=20run=5Fsafe=5Ftext=5Fextraction=20function=20fro?= =?UTF-8?q?m=2030=20to=20300=20seconds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/api/endpoints/routers/misc/document_extract.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/aymurai/api/endpoints/routers/misc/document_extract.py b/aymurai/api/endpoints/routers/misc/document_extract.py index ba315b74..ab1c61b3 100644 --- a/aymurai/api/endpoints/routers/misc/document_extract.py +++ b/aymurai/api/endpoints/routers/misc/document_extract.py @@ -34,10 +34,7 @@ def extraction(path: str) -> str: return document_normalize(text, preserve_paragraphs=True) if text else "" -def run_safe_text_extraction( - path: str, - timeout_s: float | None = 30, -) -> str: +def run_safe_text_extraction(path: str, timeout_s: float | None = 300) -> str: """ Runs the text extraction in a separate process to avoid blocking the main thread. This is useful for long-running tasks or when the extraction might hang. @@ -45,7 +42,7 @@ def run_safe_text_extraction( Args: path (str): Path to the file to be processed. timeout_s (float | None): Timeout in seconds for the extraction process. - If None, waits indefinitely. Defaults to 30. + If None, waits indefinitely. Defaults to 300. Returns: str: Extracted text from the document. From d1e81ddf42faf275a97e3da14408c9b1d4fec974 Mon Sep 17 00:00:00 2001 From: jansaldo Date: Tue, 26 May 2026 18:42:25 +0000 Subject: [PATCH 099/101] =?UTF-8?q?=F0=9F=9A=B8=20Update=20PDF=5FTOKEN=5FA?= =?UTF-8?q?LIAS=5FMAP=20with=20clearer=20aliases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aymurai/text/anonymization/pdf/common.py | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/aymurai/text/anonymization/pdf/common.py b/aymurai/text/anonymization/pdf/common.py index 91f42927..b17c22c2 100644 --- a/aymurai/text/anonymization/pdf/common.py +++ b/aymurai/text/anonymization/pdf/common.py @@ -14,22 +14,22 @@ PDF_TAG_MIN_FONT_SIZE = 7.0 PDF_TAG_FONT_STEP = 0.5 PDF_TAG_MAX_ABBREVIATION = 3 -PDF_TOKEN_ALIAS_MAP: dict[str, tuple[str, str]] = { +PDF_TOKEN_ALIAS_MAP: dict[str, tuple[str, ...]] = { "CORREO_ELECTRONICO": ("CORREO", "MAIL"), "CUIT_CUIL": ("CUIT", "CUIL"), - "DIRECCION": ("DIREC", "DIR"), - "ESTUDIOS": ("ESTUD", "EDU"), - "MARCA_AUTOMOVIL": ("MARCA_AUTO", "AUTO"), - "NACIONALIDAD": ("NACIONAL", "NAC"), - "NOMBRE_ARCHIVO": ("NOM_ARCH", "ARCH"), - "NUM_ACTUACION": ("NUM_ACT", "ACT"), - "NUM_CAJA_AHORRO": ("NUM_CAJA", "CAJA"), - "NUM_EXPEDIENTE": ("NUM_EXP", "EXPTE"), - "NUM_MATRICULA": ("NUM_MAT", "MAT"), - "PATENTE_DOMINIO": ("PAT_DOM", "PAT"), - "TELEFONO": ("TELEF", "TEL"), - "TEXTO_ANONIMIZAR": ("TEXTO_ANON", "ANON"), - "USUARIX": ("USUAR", "USR"), + "DIRECCION": ("DIR",), + "ESTUDIOS": ("EDU",), + "MARCA_AUTOMOVIL": ("VEHICULO", "AUTO"), + "NACIONALIDAD": ("PAIS", "NAC"), + "NOMBRE_ARCHIVO": ("ARCHIVO", "FILE"), + "NUM_ACTUACION": ("ACTUACION", "ACT"), + "NUM_CAJA_AHORRO": ("CAJA_AHORRO", "CAJA"), + "NUM_EXPEDIENTE": ("EXPEDIENTE", "EXPTE"), + "NUM_MATRICULA": ("MATRICULA", "MAT"), + "PATENTE_DOMINIO": ("PATENTE", "DOMINIO"), + "TELEFONO": ("TEL",), + "TEXTO_ANONIMIZAR": ("ANONIMIZAR", "TEXTO"), + "USUARIX": ("USER",), } PDF_TAG_RECT_X_PADDING = 0.5 PDF_TAG_RECT_Y_PADDING = 0.0 From e25ddcfb8f072e3c8d1494518d086356e85ee350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Ansaldo?= <48162237+jansaldo@users.noreply.github.com> Date: Tue, 26 May 2026 17:18:23 -0300 Subject: [PATCH 100/101] Fix/pdf signature anonymization (#82) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🧪 test(pdf): cover signature anonymization regressions * 🐛 fix(pdf): preserve signature appearance when redacting signer names * ✅ test(pdf): add focused signature geometry tests * ♻️ refactor(pdf): rename distance function for clarity and update references * 📝 docs(pdf): clarify signature widget flattening process in preparation function * ✅ test(pdf): cover signature review edge cases --- aymurai/text/anonymization/pdf/ops.py | 185 +++++++- aymurai/text/anonymization/pdf/sanitize.py | 12 +- aymurai/text/anonymization/pdf/widgets.py | 51 ++- .../api/routers/anonymizer/test_anonymizer.py | 398 +++++++++++++++++- 4 files changed, 591 insertions(+), 55 deletions(-) diff --git a/aymurai/text/anonymization/pdf/ops.py b/aymurai/text/anonymization/pdf/ops.py index bdad1d0a..fb5a324f 100644 --- a/aymurai/text/anonymization/pdf/ops.py +++ b/aymurai/text/anonymization/pdf/ops.py @@ -189,6 +189,104 @@ def _image_rects_for_clip( return rects +def _squared_distance_between_rect_centers( + left: pymupdf.Rect, + right: pymupdf.Rect, +) -> float: + """ + Computes the squared distance between two rectangle centers. + + Args: + left (pymupdf.Rect): The first rectangle. + right (pymupdf.Rect): The second rectangle. + + Returns: + float: The squared distance between rectangle centers. + """ + left_center = ((left.x0 + left.x1) / 2.0, (left.y0 + left.y1) / 2.0) + right_center = ((right.x0 + right.x1) / 2.0, (right.y0 + right.y1) / 2.0) + return (left_center[0] - right_center[0]) ** 2 + ( + left_center[1] - right_center[1] + ) ** 2 + + +def _refine_signature_text_rect( + page: pymupdf.Page, + entity_text: str, + widget_rect: pymupdf.Rect, + current_rect: pymupdf.Rect, +) -> pymupdf.Rect: + """ + Finds a tighter text rectangle for signer names inside signature widgets. + + Args: + page (pymupdf.Page): The PDF page being processed. + entity_text (str): The entity text being mapped. + widget_rect (pymupdf.Rect): The signature widget rectangle. + current_rect (pymupdf.Rect): The currently resolved entity rectangle. + + Returns: + pymupdf.Rect: The refined rectangle when available, otherwise current_rect. + """ + widget_clip = pymupdf.Rect(widget_rect) + hits = [ + pymupdf.Rect(hit) + for hit in page.search_for(entity_text, clip=widget_clip) + if pymupdf.Rect(hit).intersects(widget_clip) + ] + if not hits: + return pymupdf.Rect(current_rect) + + target = pymupdf.Rect(current_rect) + intersecting_hits = [hit for hit in hits if hit.intersects(target)] + candidates = intersecting_hits or hits + return pymupdf.Rect( + min( + candidates, + key=lambda hit: _squared_distance_between_rect_centers(hit, target), + ) + ) + + +def _build_signature_page_op( + page: pymupdf.Page, + entity_text: str, + widget_info: dict[str, Any], + current_rect: pymupdf.Rect, + token: str, + entity_style: dict[str, Any] | None = None, +) -> dict[str, Any]: + """ + Builds a signature-specific operation scoped to the sensitive text only. + + Args: + page (pymupdf.Page): The PDF page being processed. + entity_text (str): The sensitive text being replaced. + widget_info (dict[str, Any]): The signature widget metadata. + current_rect (pymupdf.Rect): The initially resolved text rectangle. + token (str): The logical replacement token. + entity_style (dict[str, Any] | None): The text style to render with. + + Returns: + dict[str, Any]: The signature replacement operation. + """ + refined_rect = _refine_signature_text_rect( + page, + entity_text, + widget_info["rect"], + current_rect, + ) + op = _build_page_op( + refined_rect, + None, + token, + entity_style=entity_style or widget_info.get("style") or None, + ) + op["widget_xref"] = widget_info["xref"] + op["widget_rect"] = widget_info["rect"] + return op + + def _entity_overlaps_image( page: pymupdf.Page, entity_rect: pymupdf.Rect, @@ -303,14 +401,14 @@ def _collect_page_redactions( fallback_widget["field_type"] == pymupdf.PDF_WIDGET_TYPE_SIGNATURE ): - op = _build_page_op( + op = _build_signature_page_op( + page, + entity_text, + fallback_widget, fallback_rects[0], - lines[0] if lines else None, token, entity_style=fallback_widget.get("style") or None, ) - op["widget_xref"] = fallback_widget["xref"] - op["widget_rect"] = fallback_widget["rect"] signature_widget_ops.setdefault(page_index, []).append(op) continue @@ -470,14 +568,14 @@ def _collect_page_redactions( ) continue if widget_info["field_type"] == pymupdf.PDF_WIDGET_TYPE_SIGNATURE: - op = _build_page_op( + op = _build_signature_page_op( + page, + entity_text, + widget_info, rect, - line, token, entity_style=ent_style, ) - op["widget_xref"] = widget_info["xref"] - op["widget_rect"] = widget_info["rect"] signature_widget_ops.setdefault(page_index, []).append(op) continue @@ -514,43 +612,51 @@ def _collect_page_redactions( for seg_idx, ( seg_line, - _seg_text, + seg_text, seg_rect, seg_img, seg_style, seg_widget, ) in enumerate(segments): + if signature_widget is not None: + op = _build_signature_page_op( + page, + seg_text, + signature_widget, + seg_rect, + token, + entity_style=seg_style, + ) + if seg_idx != widest_idx: + op["text"] = None + op["fontsize"] = None + signature_widget_ops.setdefault(page_index, []).append(op) + continue + if seg_idx == widest_idx: op = _build_page_op( seg_rect, seg_line, token, - is_image=(any_image and signature_widget is None), + is_image=any_image, entity_style=seg_style, ) - if signature_widget is None and shared_image_rect is not None: + if shared_image_rect is not None: op["image_rect"] = shared_image_rect else: op = _build_page_op( seg_rect, seg_line, token, - is_image=( - (seg_img is not None) and signature_widget is None - ), + is_image=(seg_img is not None), entity_style=seg_style, ) op["text"] = None op["fontsize"] = None - if seg_img is not None and signature_widget is None: + if seg_img is not None: op["image_rect"] = seg_img - if signature_widget is not None: - op["widget_xref"] = signature_widget["xref"] - op["widget_rect"] = signature_widget["rect"] - signature_widget_ops.setdefault(page_index, []).append(op) - else: - page_ops.setdefault(page_index, []).append(op) + page_ops.setdefault(page_index, []).append(op) return page_ops, widget_ops, signature_widget_ops @@ -802,6 +908,40 @@ def _apply_asset_redactions( _render_text_op(page, op) +def _apply_signature_redactions( + doc: pymupdf.Document, + signature_widget_ops: dict[int, list[dict]], +) -> None: + """ + Applies signer-name redactions without removing the full signature appearance. + + Args: + doc (pymupdf.Document): The PDF document being processed. + signature_widget_ops (dict[int, list[dict]]): The signature operations grouped by page index. + """ + for page_idx, ops in signature_widget_ops.items(): + if not ops: + continue + + page = doc[page_idx] + for op in ops: + page.add_redact_annot( + op["redact_rect"], + text=None, + fill=(1, 1, 1), + cross_out=False, + ) + + page.apply_redactions( + images=pymupdf.PDF_REDACT_IMAGE_PIXELS, + graphics=pymupdf.PDF_REDACT_LINE_ART_NONE, + text=pymupdf.PDF_REDACT_TEXT_REMOVE, + ) + + for op in ops: + _render_text_op(page, op) + + def _apply_redactions( doc: pymupdf.Document, page_ops: dict[int, list[dict]], @@ -821,8 +961,7 @@ def _apply_redactions( _prepare_signature_widget_ops(doc, signature_widget_ops) text_page_ops, asset_page_ops = _partition_page_ops(page_ops) - for page_idx, ops in signature_widget_ops.items(): - asset_page_ops.setdefault(page_idx, []).extend(ops) _apply_text_redactions(doc, text_page_ops) _apply_asset_redactions(doc, asset_page_ops) + _apply_signature_redactions(doc, signature_widget_ops) diff --git a/aymurai/text/anonymization/pdf/sanitize.py b/aymurai/text/anonymization/pdf/sanitize.py index 408f32bf..ab1bf344 100644 --- a/aymurai/text/anonymization/pdf/sanitize.py +++ b/aymurai/text/anonymization/pdf/sanitize.py @@ -96,14 +96,12 @@ def _cleanup_rect_for_signature_widget_op(op: dict[str, Any]) -> pymupdf.Rect | Returns: pymupdf.Rect | None: The cleanup rectangle for the signature widget operation, if available. """ - widget_rect = op.get("widget_rect") - if widget_rect is not None: - return pymupdf.Rect(widget_rect) - - background_rect = op.get("background_rect") or op.get("canvas_rect") - if background_rect is None: + cleanup_source = ( + op.get("redact_rect") or op.get("background_rect") or op.get("canvas_rect") + ) + if cleanup_source is None: return None - return pymupdf.Rect(background_rect) + return pymupdf.Rect(cleanup_source) def _collect_link_cleanup_rects( diff --git a/aymurai/text/anonymization/pdf/widgets.py b/aymurai/text/anonymization/pdf/widgets.py index 3ea97d7e..266a912f 100644 --- a/aymurai/text/anonymization/pdf/widgets.py +++ b/aymurai/text/anonymization/pdf/widgets.py @@ -29,21 +29,24 @@ def _signature_background_rect( Returns: pymupdf.Rect: The background rectangle for the signature replacement. """ - background = pymupdf.Rect( - op.get("line_rect") or op.get("canvas_rect") or widget_rect + background_source = ( + op.get("canvas_rect") or op.get("redact_rect") or op.get("line_rect") ) - canvas_rect = op.get("canvas_rect") - if canvas_rect is not None: - background.include_rect(pymupdf.Rect(canvas_rect)) + background = pymupdf.Rect(background_source or widget_rect) - pad_x = max(background.height * 0.75, 2.0) - pad_y = max(background.height * 0.25, 0.75) + redact_rect = op.get("redact_rect") + if redact_rect is not None: + background.include_rect(pymupdf.Rect(redact_rect)) + + # Keep the repaint area on the sensitive text line so the replacement + # background does not visually cover adjacent non-sensitive content + pad_x = max(background.height * 0.2, 0.5) widget_clip = pymupdf.Rect(widget_rect) background.x0 = max(widget_clip.x0, background.x0 - pad_x) - background.y0 = max(widget_clip.y0, background.y0 - pad_y) + background.y0 = max(widget_clip.y0, background.y0) background.x1 = min(widget_clip.x1, background.x1 + pad_x) - background.y1 = min(widget_clip.y1, background.y1 + pad_y) + background.y1 = min(widget_clip.y1, background.y1) return background @@ -272,12 +275,19 @@ def _prepare_signature_widget_ops( signature_widget_ops: dict[int, list[dict]], ) -> None: """ - Deletes signature widgets and prepares their replacement operations. + Flattens signature widgets and prepares their replacement operations. + + PyMuPDF bakes widgets at document scope, not per widget. When a + signature widget must be flattened, all widgets are intentionally baked + before sanitization so their visible appearances survive in the static + anonymized PDF. Args: doc (pymupdf.Document): The PDF document being processed. signature_widget_ops (dict[int, list[dict]]): The collected signature widget operations grouped by page index. """ + should_bake_widgets = False + for page_idx, ops in signature_widget_ops.items(): if not ops: continue @@ -300,15 +310,7 @@ def _prepare_signature_widget_ops( if widget is not None: widget_rect = pymupdf.Rect(widget.rect) - try: - page.delete_widget(widget) - except Exception as exc: - logger.warning( - "Failed to delete signature widget xref=%s on page=%s: %s", - widget_xref, - page_idx, - exc, - ) + should_bake_widgets = True else: logger.warning( "Could not resolve PDF signature widget xref=%s on page=%s", @@ -318,6 +320,13 @@ def _prepare_signature_widget_ops( for op in widget_group_ops: op["widget_rect"] = pymupdf.Rect(widget_rect) - op["asset_rect"] = pymupdf.Rect(widget_rect) - op["graphics_mode"] = pymupdf.PDF_REDACT_LINE_ART_REMOVE_IF_COVERED + op.pop("asset_rect", None) + op.pop("image_rect", None) + op.pop("graphics_mode", None) op["background_rect"] = _signature_background_rect(op, widget_rect) + + if should_bake_widgets: + try: + doc.bake(annots=False, widgets=True) + except Exception as exc: + logger.warning("Failed to flatten PDF signature widgets: %s", exc) diff --git a/tests/api/routers/anonymizer/test_anonymizer.py b/tests/api/routers/anonymizer/test_anonymizer.py index 8225e29d..0648c625 100644 --- a/tests/api/routers/anonymizer/test_anonymizer.py +++ b/tests/api/routers/anonymizer/test_anonymizer.py @@ -13,14 +13,20 @@ from aymurai.database.schema import AnonymizationParagraph from aymurai.database.utils import text_to_uuid +from aymurai.meta.api_interfaces import LabelPolicy, RenderPolicy from aymurai.text.anonymization import DocxAnonymizer, PdfAnonymizer, get_anonymizer from aymurai.text.anonymization.alignment import index_paragraphs +from aymurai.text.anonymization.pdf.ops import _refine_signature_text_rect +from aymurai.text.anonymization.pdf.widgets import _signature_background_rect from tests.api.conftest import build_label from tests.api.routers.conftest import build_mock_pipeline PNG_1X1 = base64.b64decode( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a6R8AAAAASUVORK5CYII=" ) +PNG_BLACK_1X1 = base64.b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR42mNgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==" +) WATERMARK_URL = "https://www.aymurai.info/" WINDOWS_PYMUPDF_LAYOUT_XFAIL = pytest.mark.xfail( @@ -50,6 +56,7 @@ def _run_pdf_anonymizer( source_path: Path, document: str, labels: list[dict], + render_context: dict | None = None, ) -> Path: output_dir = tmp_path / "out" output_dir.mkdir(exist_ok=True) @@ -57,10 +64,174 @@ def _run_pdf_anonymizer( {"path": str(source_path)}, [{"document": document, "labels": labels}], str(output_dir), + render_context=render_context, ) return Path(output_path) +def _label_for_document_text(document: str, text: str, label: str = "PER") -> dict: + payload = _label_dict(text, label) + start = document.index(text) + payload["start_char"] = start + payload["end_char"] = start + len(text) + return payload + + +def _render_context_for_entities(labels: list[dict]) -> dict: + index_by_entity = {} + next_index_by_base = {} + for label in labels: + attrs = label.get("attrs") or {} + base = attrs.get("aymurai_label") or label.get("label") or "ENT" + entity_id = str(attrs.get("canonical_entity_id") or label.get("text")) + key = (base, entity_id) + if key not in index_by_entity: + next_index_by_base[base] = next_index_by_base.get(base, 0) + 1 + index_by_entity[key] = next_index_by_base[base] + + return { + "render_policy": RenderPolicy(suffix_mode="always", suffix_threshold=0), + "label_policies": {"PER": LabelPolicy()}, + "count_by_base": dict(next_index_by_base), + "index_by_entity": index_by_entity, + } + + +def _dark_pixel_ratio(page: pymupdf.Page, rect: pymupdf.Rect) -> float: + pixmap = page.get_pixmap( + matrix=pymupdf.Matrix(2, 2), + clip=rect, + alpha=False, + ) + samples = pixmap.samples + if not samples: + return 0.0 + + channels = pixmap.n + dark_pixels = 0 + total_pixels = pixmap.width * pixmap.height + for offset in range(0, len(samples), channels): + if all(channel < 96 for channel in samples[offset : offset + 3]): + dark_pixels += 1 + + return dark_pixels / max(total_pixels, 1) + + +def _assert_text_count(page_text: str, text: str, expected: int) -> None: + assert page_text.count(text) == expected, page_text + + +def _assert_rect_close(actual: pymupdf.Rect, expected: pymupdf.Rect) -> None: + assert (actual.x0, actual.y0, actual.x1, actual.y1) == pytest.approx( + (expected.x0, expected.y0, expected.x1, expected.y1) + ) + + +def _write_variable_signature_pdf( + path: Path, +) -> tuple[Path, list[dict], list[str], list[str], list[pymupdf.Rect]]: + blocks = [ + { + "origin": (58, 112), + "lines": [ + "Mesa de Control 42", + "Adriana Morales", + "Area de Validacion", + "Codigo A-17", + ], + "signer": "Adriana Morales", + "qr": "top", + }, + { + "origin": (326, 112), + "lines": [ + "Bernardo Diaz", + "Direccion Legal", + "Organismo Beta Sur", + "Tramite BX-900", + ], + "signer": "Bernardo Diaz", + "qr": "right", + }, + { + "origin": (58, 328), + "lines": [ + "Centro Operativo", + "Carolina Ruiz", + "Secretaria Tecnica", + "2026-05-26 10:15", + ], + "signer": "Carolina Ruiz", + "qr": "left", + }, + { + "origin": (326, 328), + "lines": [ + "Unidad Regional", + "Coordinacion de Revision", + "Daniel Silva", + "Expediente Digital Z-42", + ], + "signer": "Daniel Silva", + "qr": "top", + }, + { + "origin": (58, 544), + "lines": [ + "Responsable: Elena Torres - Acta Final", + "Delegacion Gamma", + "Registro Interno R-204", + ], + "signer": "Elena Torres", + "qr": "right", + }, + ] + + doc = pymupdf.open() + page = doc.new_page() + preds: list[dict] = [] + preserved_texts: list[str] = [] + signers: list[str] = [] + qr_rects: list[pymupdf.Rect] = [] + + for idx, block in enumerate(blocks): + x, y = block["origin"] + if block["qr"] == "right": + qr_rect = pymupdf.Rect(x + 150, y - 4, x + 182, y + 28) + elif block["qr"] == "left": + qr_rect = pymupdf.Rect(x - 2, y - 48, x + 30, y - 16) + else: + qr_rect = pymupdf.Rect(x, y - 52, x + 32, y - 20) + page.insert_image(qr_rect, stream=PNG_BLACK_1X1) + qr_rects.append(qr_rect) + + for line_idx, line in enumerate(block["lines"]): + page.insert_text((x, y + (line_idx * 16)), line, fontsize=11) + if line == block["signer"]: + continue + if block["signer"] in line: + preserved_texts.extend( + part.strip() for part in line.split(block["signer"]) if part.strip() + ) + else: + preserved_texts.append(line) + + widget = pymupdf.Widget() + widget.field_name = f"sig_{idx}" + widget.field_type = pymupdf.PDF_WIDGET_TYPE_SIGNATURE + widget.rect = pymupdf.Rect(x - 12, y - 62, x + 230, y + 64) + page.add_widget(widget) + + document = "\n".join(block["lines"]) + label = _label_for_document_text(document, block["signer"]) + preds.append({"document": document, "labels": [label]}) + signers.append(block["signer"]) + + doc.save(path) + doc.close() + return path, preds, signers, preserved_texts, qr_rects + + @pytest.mark.integration def test_anonymization_package_exports_and_registry_are_stable(): assert PdfAnonymizer.__name__ == "PdfAnonymizer" @@ -218,17 +389,234 @@ def test_pdf_anonymizer_removes_image_backed_entities(tmp_path): assert "" in page_text +def test_signature_background_rect_stays_on_signer_name_line(): + background = _signature_background_rect( + { + "line_rect": pymupdf.Rect(80, 70, 220, 118), + "canvas_rect": pymupdf.Rect(112, 70, 145, 82), + "redact_rect": pymupdf.Rect(112, 70, 145, 82), + }, + pymupdf.Rect(60, 60, 230, 130), + ) + + assert background.y0 >= 70 + assert background.y1 <= 82 + + +def test_signature_text_rect_refinement_does_not_include_role_text(tmp_path): + source_path = _write_pdf( + tmp_path / "signature-role.pdf", + lambda _doc, page: ( + page.insert_text((100, 80), "RUIZ"), + page.insert_text((100, 96), "JUEZ/A"), + ), + ) + + with pymupdf.open(source_path) as doc: + page = doc[0] + signer_rect = page.search_for("RUIZ")[0] + role_rect = page.search_for("JUEZ/A")[0] + loose_rect = pymupdf.Rect(signer_rect) + loose_rect.include_rect(role_rect) + + refined = _refine_signature_text_rect( + page, + "RUIZ", + pymupdf.Rect(80, 60, 200, 115), + loose_rect, + ) + + assert refined.intersects(signer_rect) + assert not refined.intersects(role_rect) + + +def test_signature_text_rect_refinement_returns_current_rect_when_no_hit_in_widget( + tmp_path, +): + source_path = _write_pdf( + tmp_path / "signature-no-hit.pdf", + lambda _doc, page: page.insert_text((260, 80), "RUIZ"), + ) + + with pymupdf.open(source_path) as doc: + page = doc[0] + current_rect = pymupdf.Rect(100, 72, 130, 84) + + refined = _refine_signature_text_rect( + page, + "RUIZ", + pymupdf.Rect(80, 60, 180, 115), + current_rect, + ) + + _assert_rect_close(refined, current_rect) + + +def test_signature_text_rect_refinement_selects_closest_matching_hit(tmp_path): + source_path = _write_pdf( + tmp_path / "signature-multiple-hits.pdf", + lambda _doc, page: ( + page.insert_text((100, 80), "RUIZ"), + page.insert_text((220, 80), "RUIZ"), + ), + ) + + with pymupdf.open(source_path) as doc: + page = doc[0] + left_rect, right_rect = page.search_for("RUIZ") + target = pymupdf.Rect(right_rect) + target.x0 += 2 + target.x1 += 2 + + refined = _refine_signature_text_rect( + page, + "RUIZ", + pymupdf.Rect(80, 60, 280, 115), + target, + ) + + assert refined.intersects(right_rect) + assert not refined.intersects(left_rect) + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_only_redacts_marked_signature_names_in_variable_layouts( + tmp_path, +): + source_path, preds, signers, preserved_texts, qr_rects = ( + _write_variable_signature_pdf(tmp_path / "variable-signatures.pdf") + ) + render_context = _render_context_for_entities([pred["labels"][0] for pred in preds]) + output_dir = tmp_path / "out-variable" + output_dir.mkdir(exist_ok=True) + + output_path = PdfAnonymizer().anonymize( + {"path": str(source_path)}, + preds, + str(output_dir), + render_context=render_context, + ) + + with pymupdf.open(output_path) as output_doc: + page = output_doc[0] + page_text = page.get_text() + + assert list(page.widgets() or []) == [] + assert len(page.get_image_info()) >= len(qr_rects) + + for signer in signers: + assert signer not in page_text + + for index in range(1, len(signers) + 1): + assert f"" in page_text + + for preserved_text in preserved_texts: + _assert_text_count(page_text, preserved_text, 1) + + for qr_rect in qr_rects: + assert _dark_pixel_ratio(page, qr_rect) > 0.25 + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_leaves_unlabeled_signature_names_visible(tmp_path): + source_path, preds, signers, preserved_texts, qr_rects = ( + _write_variable_signature_pdf(tmp_path / "partially-labeled-signatures.pdf") + ) + unlabeled_signer = signers[-1] + filtered_preds = [] + filtered_labels = [] + for pred, signer in zip(preds, signers, strict=True): + labels = [] if signer == unlabeled_signer else pred["labels"] + filtered_preds.append({**pred, "labels": labels}) + filtered_labels.extend(labels) + + render_context = _render_context_for_entities(filtered_labels) + output_dir = tmp_path / "out-partial" + output_dir.mkdir(exist_ok=True) + + output_path = PdfAnonymizer().anonymize( + {"path": str(source_path)}, + filtered_preds, + str(output_dir), + render_context=render_context, + ) + + with pymupdf.open(output_path) as output_doc: + page = output_doc[0] + page_text = page.get_text() + + assert list(page.widgets() or []) == [] + assert unlabeled_signer in page_text + assert "" not in page_text + + for index, signer in enumerate(signers[:-1], start=1): + assert signer not in page_text + assert f"" in page_text + + for preserved_text in preserved_texts: + _assert_text_count(page_text, preserved_text, 1) + + for qr_rect in qr_rects: + assert _dark_pixel_ratio(page, qr_rect) > 0.25 + + +@pytest.mark.integration +@WINDOWS_PYMUPDF_LAYOUT_XFAIL +def test_pdf_anonymizer_preserves_non_signature_widget_appearance_when_baking( + tmp_path, +): + def configure(_doc: pymupdf.Document, page: pymupdf.Page) -> None: + page.insert_text((80, 88), "Ana Perez") + + text_widget = pymupdf.Widget() + text_widget.field_name = "public_field" + text_widget.field_type = pymupdf.PDF_WIDGET_TYPE_TEXT + text_widget.field_value = "Visible Field Value" + text_widget.text_font = "Helv" + text_widget.text_fontsize = 10 + text_widget.rect = pymupdf.Rect(260, 70, 410, 96) + page.add_widget(text_widget) + + signature_widget = pymupdf.Widget() + signature_widget.field_name = "sig_1" + signature_widget.field_type = pymupdf.PDF_WIDGET_TYPE_SIGNATURE + signature_widget.rect = pymupdf.Rect(60, 60, 180, 110) + page.add_widget(signature_widget) + + source_path = _write_pdf(tmp_path / "signature-and-text-widget.pdf", configure) + output_path = _run_pdf_anonymizer( + tmp_path, + source_path, + "Ana Perez", + [_label_dict("Ana Perez")], + ) + + with pymupdf.open(output_path) as output_doc: + page = output_doc[0] + page_text = page.get_text() + + assert list(page.widgets() or []) == [] + assert "Visible Field Value" in page_text + assert "Ana Perez" not in page_text + assert "" in page_text + + @pytest.mark.integration @WINDOWS_PYMUPDF_LAYOUT_XFAIL -def test_pdf_anonymizer_removes_signature_widgets_without_restoring_appearance( +def test_pdf_anonymizer_preserves_signature_appearance_when_redacting_signer_name( tmp_path, ): def configure(_doc: pymupdf.Document, page: pymupdf.Page) -> None: - page.insert_text((80, 90), "Ana Perez") + page.insert_text((80, 76), "FIRMADO DIGITALMENTE") + page.insert_text((80, 92), "05/02/2025 14:17") + page.insert_text((80, 108), "Ana Perez") + page.insert_image(pymupdf.Rect(185, 68, 215, 98), stream=PNG_1X1) widget = pymupdf.Widget() widget.field_name = "sig_1" widget.field_type = pymupdf.PDF_WIDGET_TYPE_SIGNATURE - widget.rect = pymupdf.Rect(60, 60, 220, 110) + widget.rect = pymupdf.Rect(60, 60, 230, 120) page.add_widget(widget) source_path = _write_pdf(tmp_path / "signature.pdf", configure) @@ -244,7 +632,9 @@ def configure(_doc: pymupdf.Document, page: pymupdf.Page) -> None: page_text = page.get_text() assert list(page.widgets() or []) == [] - assert page.get_image_info() == [] + assert page.get_image_info() != [] + assert "FIRMADO DIGITALMENTE" in page_text + assert "05/02/2025 14:17" in page_text assert "Ana Perez" not in page_text assert "" in page_text From f2310e25c997a81a9c992facf4f43938b179a6f9 Mon Sep 17 00:00:00 2001 From: conrabeatriz Date: Wed, 27 May 2026 16:03:47 -0300 Subject: [PATCH 101/101] =?UTF-8?q?=F0=9F=90=9B=20Bug=20fix=20for=20exact?= =?UTF-8?q?=20entities.=20(#80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 Bug fixed for entities who are always the same that have to bypass the fuzzy matching algorithm. * ⚡️ Improved structure following copilot comments. * ⚗️ Experimentation. * 🐛 Merge duplicate labels for the same span and AymurAI label in _dedupe_doclabels function * ✅ Add integration test for merging cached duplicate labels for the same span --------- Co-authored-by: jansaldo --- .../routers/anonymizer/anonymizer.py | 44 ++++++++++++---- .../anonymization_postprocess/core.py | 16 ++++++ .../anonymization_postprocess/exact_labels.py | 10 ++++ aymurai/utils/entity_disambiguation/fuzzy.py | 43 ++++++++++++--- .../10-anonymize-document-render-policy.ipynb | 9 +++- .../api/routers/anonymizer/test_anonymizer.py | 52 +++++++++++++++++++ 6 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 aymurai/transforms/anonymization_postprocess/exact_labels.py diff --git a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py index 6bc7b494..6fde1db7 100644 --- a/aymurai/api/endpoints/routers/anonymizer/anonymizer.py +++ b/aymurai/api/endpoints/routers/anonymizer/anonymizer.py @@ -89,30 +89,54 @@ def _entities_to_doclabels(entities: list[dict]) -> list[DocLabel]: def _dedupe_doclabels(labels: Iterable[DocLabel]) -> list[DocLabel]: """ - Remove exact duplicate labels while preserving first-seen order. + Merge duplicate labels for the same span and AymurAI label. Args: labels (Iterable[DocLabel]): An iterable of DocLabel objects, potentially containing duplicates. Returns: - list[DocLabel]: A list of DocLabel objects with duplicates removed, + list[DocLabel]: A list of DocLabel objects with duplicates merged, preserving the order of first occurrence. """ deduped: list[DocLabel] = [] - seen: set[str] = set() + index_by_key: dict[tuple[int, int, str], int] = {} for label in labels: - key = json.dumps( - label.model_dump(mode="json", exclude_none=True), - sort_keys=True, - separators=(",", ":"), + key = ( + label.start_char, + label.end_char, + label.attrs.aymurai_label if label.attrs else "", ) - if key in seen: + existing_index = index_by_key.get(key) + if existing_index is None: + index_by_key[key] = len(deduped) + deduped.append(label) continue - seen.add(key) - deduped.append(label) + existing = deduped[existing_index] + existing_data = existing.model_dump(mode="json") + incoming_data = label.model_dump(mode="json") + existing_attrs = existing_data.get("attrs") or {} + incoming_attrs = incoming_data.get("attrs") or {} + + for attr_key, incoming_value in incoming_attrs.items(): + existing_value = existing_attrs.get(attr_key) + if attr_key == "aymurai_label_subclass": + merged = list(existing_value or []) + for subclass in incoming_value or []: + if subclass not in merged: + merged.append(subclass) + existing_attrs[attr_key] = merged + elif existing_value in (None, [], "") and incoming_value not in ( + None, + [], + "", + ): + existing_attrs[attr_key] = incoming_value + + existing_data["attrs"] = existing_attrs + deduped[existing_index] = DocLabel.model_validate(existing_data) return deduped diff --git a/aymurai/transforms/anonymization_postprocess/core.py b/aymurai/transforms/anonymization_postprocess/core.py index bd596d29..af5185e4 100644 --- a/aymurai/transforms/anonymization_postprocess/core.py +++ b/aymurai/transforms/anonymization_postprocess/core.py @@ -4,6 +4,7 @@ from aymurai.meta.pipeline_interfaces import Transform from aymurai.meta.types import DataItem from aymurai.utils.misc import get_element +from aymurai.transforms.anonymization_postprocess.exact_labels import EXACT_LABELS class AnonymizationEntityCleaner(Transform): @@ -32,6 +33,7 @@ def process(self, ent: dict) -> dict: original_text = ent["text"] start_char = ent["start_char"] end_char = ent["end_char"] + label = ent["attrs"]["aymurai_label"] # Match leading and trailing non-alphanumeric characters leading_match = re.match(r"^\W+", original_text) @@ -47,10 +49,24 @@ def process(self, ent: dict) -> dict: if not cleaned_text: return None + raw_subclass = ent["attrs"]["aymurai_label_subclass"] + if isinstance(raw_subclass, list): + aymurai_label_subclass = raw_subclass.copy() + elif raw_subclass: + aymurai_label_subclass = [raw_subclass] + else: + aymurai_label_subclass = [] + + if label in EXACT_LABELS: + flattened_text = re.sub(r"[^a-zA-Z0-9]", "", cleaned_text) + if flattened_text and flattened_text not in aymurai_label_subclass: + aymurai_label_subclass.append(flattened_text) + # Update the entity's alt text and indices ent["attrs"]["aymurai_alt_text"] = cleaned_text ent["attrs"]["aymurai_alt_start_char"] = start_char + leading_chars_removed ent["attrs"]["aymurai_alt_end_char"] = end_char - trailing_chars_removed + ent["attrs"]["aymurai_label_subclass"] = aymurai_label_subclass return ent diff --git a/aymurai/transforms/anonymization_postprocess/exact_labels.py b/aymurai/transforms/anonymization_postprocess/exact_labels.py new file mode 100644 index 00000000..afe345da --- /dev/null +++ b/aymurai/transforms/anonymization_postprocess/exact_labels.py @@ -0,0 +1,10 @@ +EXACT_LABELS = { + "DNI", + "CUIT_CUIL", + "TELEFONO", + "PATENTE_DOMINIO", + "IP", + "NUM_CAJA_AHORRO", + "CBU", + "NUM_MATRICULA", +} diff --git a/aymurai/utils/entity_disambiguation/fuzzy.py b/aymurai/utils/entity_disambiguation/fuzzy.py index 682810b6..e55850d1 100644 --- a/aymurai/utils/entity_disambiguation/fuzzy.py +++ b/aymurai/utils/entity_disambiguation/fuzzy.py @@ -7,6 +7,8 @@ from aymurai.meta.api_interfaces import DocLabel from aymurai.meta.entities import CanonicalEntity +from aymurai.transforms.anonymization_postprocess.exact_labels import EXACT_LABELS + def _find_parent(parent: list[int], idx: int) -> int: """ @@ -179,16 +181,45 @@ def build_canonical_entities( if target_labels and attrs.aymurai_label not in target_labels: continue alias = attrs.aymurai_alt_text or label.text + + subclass_val = getattr(attrs, "aymurai_label_subclass", None) + + if isinstance(subclass_val, list): + exact_alias = subclass_val[-1] if subclass_val else alias + else: + exact_alias = subclass_val or alias + grouped.setdefault(attrs.aymurai_label, []).append( - {"text": alias, "aymurai_label": attrs.aymurai_label} + { + "text": alias, + "aymurai_label": attrs.aymurai_label, + "exact_alias": exact_alias, + } ) canonical_entities: list[CanonicalEntity] = [] - for items in grouped.values(): - clusters = _cluster_aliases_with_cdist( - items=items, - threshold=threshold, - ) + for label_type, items in grouped.items(): + if label_type in EXACT_LABELS: + exact_groups = {} + for item in items: + exact_groups.setdefault(item["exact_alias"], []).append(item) + clusters = [ + [ + ( + item["text"], + str(item["exact_alias"]).lower().strip(), + item["aymurai_label"], + ) + for item in group_items + ] + for group_items in exact_groups.values() + ] + else: + clusters = _cluster_aliases_with_cdist( + items=items, + threshold=threshold, + ) + canonical_entities.extend(_clusters_to_canonical_entities(clusters)) canonical_entities = sorted(canonical_entities, key=lambda x: x.canonical_text) diff --git a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb index c072066f..92bc8185 100644 --- a/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb +++ b/notebooks/experiments/entity-disambiguation/10-anonymize-document-render-policy.ipynb @@ -64,7 +64,7 @@ "\n", "print(f\"Found {len(documents)} documents\")\n", "\n", - "doc_path = documents[14]\n", + "doc_path = documents[5]\n", "print(f\"Processing document: {doc_path}\")" ] }, @@ -430,6 +430,13 @@ "result = group_alt_texts_by_entity(anonymize_labels_fuzzy)\n", "print(json.dumps(result, indent=4, ensure_ascii=False))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/tests/api/routers/anonymizer/test_anonymizer.py b/tests/api/routers/anonymizer/test_anonymizer.py index 0648c625..4480e05f 100644 --- a/tests/api/routers/anonymizer/test_anonymizer.py +++ b/tests/api/routers/anonymizer/test_anonymizer.py @@ -935,6 +935,58 @@ def test_should_dedupe_duplicate_labels_when_returning_cached_prediction( mock_load_pipeline.assert_not_called() +@pytest.mark.integration +@patch("aymurai.api.endpoints.routers.anonymizer.anonymizer.load_pipeline") +def test_should_merge_cached_duplicate_labels_for_same_span_and_label( + mock_load_pipeline, client, db_session +): + text = ( + "Víctima: María Paula Trucha, DNI 23.456.789, quien se encuentra " + "conectada con su cámara apagada." + ) + dni_label = build_label("DNI", "23.456.789").model_dump(mode="json") + dni_label.update({"start_char": 33, "end_char": 43}) + dni_label["attrs"].update( + { + "aymurai_alt_text": "23.456.789", + "aymurai_alt_start_char": 33, + "aymurai_alt_end_char": 43, + "aymurai_label_instance": 2, + "aymurai_disambiguation": "fuzzy", + "aymurai_anonymize": True, + "canonical_entity_id": "0bba6d15-1b0c-51f0-b2ca-4fdc8a57cb73", + } + ) + enriched_dni_label = { + **dni_label, + "attrs": { + **dni_label["attrs"], + "aymurai_label_subclass": ["23456789"], + }, + } + + db_session.add( + AnonymizationParagraph( + id=text_to_uuid(text), + text=text, + prediction=[dni_label, enriched_dni_label], + ) + ) + db_session.commit() + + response = client.post( + "/anonymizer/predict", + json={"text": text}, + params={"use_cache": True}, + ) + + assert response.status_code == 200 + data = response.json() + assert data["document"] == text + assert data["labels"] == [enriched_dni_label] + mock_load_pipeline.assert_not_called() + + @pytest.mark.integration @patch( "aymurai.api.endpoints.routers.anonymizer.anonymizer.map_canonical_entities_ner_preds"

Fu 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�Om36J0#9XA;T+uy?+ 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

2cNtN1h^8{7D2%K!JmU20y@Ek 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�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